F Find the AFei Numbers

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

题目描述

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 ).

输出描述:

For the last T lines, output the total numbers of AFei numbers that are not greater than n.
示例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.

题意:给定一个自然数n(0<=n<=1e18),求[0,n]里有多少个整数包含“520”(即所谓Afei数的数量)

解题思路: 数位DP入门题。

首先,设dp[i]表示长度为i的AFei数的数量。发现这样不行,因为按数位计算的话,每次只会考虑一位,而AFei 数的特征是要有连续的三位,并且第一位是5,第二位是2,第三位是0。

这说明需要记录前两位,那么设dp[i][j]表示以j开头,后面还有i位的AFei数的数量。比如dp[4][25]就是表示形 如25****的数字中,AFei数的数量。因为n只有1e18,所以i最大不超过19,而j是用来记录前两位的,所以j最 大不超过99,所以声明dp数组的时候只需要long long dp[20][100]即可。

AFei数并没有规定“520”的数量有多少个,碰到这样的我习惯性反着算,也就是用dp[i][j]表示以j开头,后面 还有i位的不是AFei数的数量。算到最后再减一下就行了。


状态转移方程:


举个例子:以f(****)表示形如****的不是afei数的数量,显然:f[25****]=f[50***]+f[51***]+f[52***]+f[53***]+f[54***]+f[55***]+f[56***]+f[57***]+f[58***]+f[59***]也就是dp[4][25] = σ?=0
9 ??[3] [ 25%1010 + k]

解题思路(续) 解决了dp[i][j]的问题了,再来考虑如何让我们按位递推的时候的数字不超过n~~ 假设当前算到第i位,接下来是i-1位,那么就有以下三个要求: (1)如果前两位是52,那么这一位不能是0 (2)如果前面所有位和n的前几位一模一样,那么这一位的数字不能超过n的这一位

(3)如果前两位不是52并且前面的位和n的前几位不完全一样,那么这一位数字可以是0~9的任意一个数

按位递推的时候,只要满足了以上三个要求,就可以保证递推到最后一位的时候的数字是不超过n的非Afei数

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int temp[20];
ll dp[20][4];
ll dfs(int len, int s, bool limit)
{
    if(len == 0)
        return s==3;
    if(!limit && dp[len][s] != -1)
        return dp[len][s];
    int up = limit ? temp[len] : 9;
    ll sum = 0;
    for(int i = 0; i <= up; i ++)
    {
        int ss;
        if(s==3)
            ss=3;
        else if(i==5)
            ss=1;
        else if(s==1&&i==2)
            ss=2;
        else if(s==2&&i==0)
            ss=3;
        else
            ss=0;
        sum += dfs(len - 1, ss, limit && (i == up));
    }
    if(!limit)
        dp[len][s] = sum;
    return sum;
}
ll solve(ll x)
{
    int len = 0;
    while(x)
    {
        temp[++ len] = x % 10;
        x /= 10;
    }
    return dfs(len, 0, 1);
}
int main()
{
    memset(dp,-1,sizeof(dp));
    int T;
    scanf("%d", &T);
    while(T --)
    {
        ll b;
        scanf("%lld", &b);
        printf("%lld\n", solve(b));
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/DWVictor/p/10229999.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值