湖南大学第十四届ACM程序设计新生杯(重现赛)- FFind the AFei Numbers(数位dp)

题目链接

题目描述

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.

输入

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.

思路
  1. 计算不包括520的数字
  • dp[ i ][ j ] 表示以数字 j 结尾后面剩余 i 位数字中不包括520的数字的数量
  • dp[ i ][ j ] = ∑ k = 0 k = t o p \sum_{k=0}^{k=top} k=0k=topdp[ i - 1 ][ (j % 10) * 10 + k ]
  1. 计算包括520的数字
  • dp[ i ][ j ][ flag ] 表示以数字 j 结尾后面剩余 i 位数字中包括520的数字的数量,其中flag表示走到当前位置是否出现过520
  • 为什么加上flag表示? 如果520出现过,递归到0的时候就要+1,否者不加。
// 计算不包括
#include <bits/stdc++.h>
#define LL  long long
#define P pair<int, int>
#define lowbit(x) (x & -x)
#define mem(a, b) memset(a, b, sizeof(a))
#define REP(i, n) for (int i = 1; i <= (n); ++i)
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define maxn 100005
using namespace std;

int num[20];
LL dp[20][100];
LL dfs(int len, int pre, int limit) {
    if (!len)   return 1;
    // 使用dp数组,前提limit = 0
    if (!limit && dp[len][pre] != -1) return dp[len][pre];
    LL sum = 0;
    int top = 9;
    if (limit)  top = num[len];
    for (int i = 0; i <= top; ++i) {
        if (i == 0 && pre == 52)    continue;
        sum += dfs(len-1, (pre%10) * 10 + i, limit&&i==top);
    }
    // 只有在limit = 0 时,更新dp
    if (!limit) dp[len][pre] = sum;
    return sum;
}

LL solve(LL n) {
    int len = 0;
    LL t = n;
    while (t) {
        num[++len] = t % 10;
        t /= 10;
    }
    LL ans = dfs(len, 0, 1);
    ans = n + 1 - ans;
    printf("%lld\n", ans);
    return 0;
}

int main() {
#ifndef ONLINE_JUDGE
   freopen("in.txt", "r", stdin);
   // freopen("out.txt", "w", stdout);
#endif
    mem(dp, -1);
    int T;
    scanf("%d", &T);
    while (T--) {
        LL n;
        scanf("%lld", &n);
        solve(n);
    }

    return 0;
}
// 计算包括
#include <bits/stdc++.h>
#define LL  long long
#define P pair<int, int>
#define lowbit(x) (x & -x)
#define mem(a, b) memset(a, b, sizeof(a))
#define REP(i, n) for (int i = 1; i <= (n); ++i)
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define maxn 100005
using namespace std;

int num[20];
LL dp[20][100][2];
LL dfs(int len, int pre, int limit, int flag) {
    if (!len)   return flag ? 1 : 0;
    if (!limit && dp[len][pre][flag] != -1)   return dp[len][pre][flag];
    int top = limit ? num[len] : 9;
    LL sum = 0;
    for (int i = 0; i <= top; ++i) {
        if (i == 0 && pre == 52) sum += dfs(len-1, (pre%10)*10+i, limit&&i==top, 1);
        else sum += dfs(len-1, (pre%10)*10+i, limit&&i==top, flag);
    }
    if (!limit) dp[len][pre][flag] = sum;
    return sum;
}

LL solve(LL n) {
    int len = 0;
    LL t = n;
    while (t) {
        num[++len] = t % 10;
        t /= 10;
    }
    LL ans = dfs(len, 0, 1, 0);
    printf("%lld\n", ans);
    return 0;
}

int main() {
#ifndef ONLINE_JUDGE
   freopen("in.txt", "r", stdin);
   // freopen("out.txt", "w", stdout);
#endif
    mem(dp, -1);
    int T;
    scanf("%d", &T);
    while (T--) {
        LL n;
        scanf("%lld", &n);
        solve(n);
    }

    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值