湖南大学第十四届ACM程序设计大赛 F Find the AFei Numbers

链接:https://ac.nowcoder.com/acm/contest/338/F
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

AFei loves numbers. He defines the natural number containing "520" as the AFei number, such as 1234520, 8752012 and 5201314. Now he wants to know how many AFei numbers are not greater than n.

输入描述:

The first line contains an integer T (1 <= T <= 100 ).

The following T lines contain an interger n ( 0 <= n <= 1e18 ).

输出描述:

The first line contains an integer T (1 <= T <= 100 ).

The following T lines contain an interger n ( 0 <= n <= 1e18 ).

示例1

输入

复制

2
1000
5520

输出

复制

1
16

说明

For the first case, only 520 is AFei number.

For the second case, 520,1520, 2520, 3520, 4520, 5200, 5201, 5202, 5203, 5204, 5205, 5206, 5207, 5208, 5209 and 5520 are AFei number. So there are 16 AFei numbers.

题目大意:

题目描述 :

AFEI喜欢数字。他将包含“520”的自然数定义为AFEI数,例如1234520、8752012和5201314。现在他想知道有多少个afei数不大于n。

输入描述:

第一行包含整数t(1<=t<=100)。

输出描述:
对于最后一条T线,输出不大于N的AFEI总数。

分析:

这个题的数据范围是1e18,暴力的话肯定会超时。运用数位DP的话相对容易,但不太好想。发现运用DFS的话相对较容易理解些,运用DFS首先要先将数据的数位拆分。从最低位开始,在单个数位值没有到上限时,可以先换到下一位。换到最高位时,就返回1.然后倒回去逐个尝试每个不能组成520的数进行加和。最后用总数减去加和数即为结果。

代码的有些注释可能解释不当,恳请指正!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll digit[22];//定义digit用于记录每位数字
ll dp[22][10][10];
  
ll dfs(int len, int if5, int if2, int limit) 
{
    if(len == 0)//当len为0时此时已经搜索到了最高位,这个时候只有1种情况了,即520xxxxx。同时已经
    {           //搜索到了最高位,不能再往更高位搜索了。到这里就结束了,返回1即可。
        return 1;//这个返回值用来累计1;
    }
    if(!limit && dp[len][if5][if2]) 
    {
        return dp[len][if5][if2];
    }
    ll res = 0, up_bound = (limit ? digit[len] : 9);//定义一个up_bound用于限制对应每个位能
     //尝试的最大值,这是针对最低位的考虑,如果不是最低位,limit肯定是0,那么最低位的下一位肯定
     //能尝试的数据范围是0-9
    for(int i = 0; i <= up_bound; i++)//对每个位枚举一个数 
    {
        if(if5 == 5 && if2 == 2 && i == 0)//看是否能与下一位和下下一位组成520
        {
            continue;//如果成立,则此次不算,因为我们要求的是所有不能组成520的数
        }
        res += dfs(len - 1, if2 , i, limit && i == up_bound);//如果不成立,则对这个位数的
    } //枚举可以先放一放,直接先进入更高位数的枚举,把对应尝试的i变为尝试数的十位,之前尝试数
      //十位变尝试数百位,在i!=up_bound的情况下,下一位肯定能尝试的数为0-9,所以limit必为0
    if(!limit) 
    {
        dp[len][if5][if2] = res;//当这一位所有的数都枚举了对应这一位的到最高位累加的数赋值
    }                           //给对应的dp[len];
    return res;//对应数位全部的可能已经枚举,返回给低位累加,让低位继续尝试。
}
  
ll solve(ll n) 
{
    int cnt = 0;
    while(n) 
    {
        digit[++cnt] = n % 10;//将每位数字存在digit数组中
        n /= 10;
    }
    return dfs(cnt, 0, 0, 1);//cnt用来存放数组位数,if5用来存放百位数字(尝试用)
}                   //if3用来存放十位数字(尝试用)由于初始是从最低位开始的所以个位十位暂定为0
  
int main() 
{
    int t;
    cin >> t;
    while(t--) 
    {
        ll n;
        cin >> n;
        cout << n - solve(n) + 1<< endl;//将总数减去不可能组合成为520的数剩下的即为可以组合出
    }                                    //520的数
      
    return 0;
}

猜你喜欢

转载自blog.csdn.net/basketball616/article/details/86242207