HDU 3555 Bomb

题目链接:HDU 3555 Bomb

题目大意:
输出\([1, n]\)内所有含有\(49\)的数的个数。

题解:
明显的数位\(dp\)题。
\(dp[i][j]\):长度为\(i\)的数的第\(j\)种状态:
\(dp[i][0]\):长度为\(i\),高位不包含\(49\)且第\(i\)位不是\(4\)的个数;
\(dp[i][1]\):长度为\(i\),高位不包含\(49\)但是第\(i\)位为\(4\)的个数;
\(dp[i][2]\):长度为\(i\),高位包含\(49\)的个数。
接着就是从高位开始统计。

#include <iostream>
#include <cstring>
using namespace std;
#define ll long long

ll n, dp[25][3];
int digit[25]; // 数字的每一位

ll dfs(int pos, int status, bool limit) {
    if (pos <= 0) { // 如果到了已经枚举了最后一位,并且在枚举的过程中有49序列出现
        return status == 2;
    }
    if (!limit && dp[pos][status]) { // 没有限制时用记忆化搜索
        return dp[pos][status];
    }
    ll ans = 0;
    int up = limit ? digit[pos] : 9; // 确定这一位的上限
    for (int i = 0; i <= up; ++i) { // 每一位有这么多的选择
        int newStatus = status;
        if (status == 0 && i == 4) // 高位不含49并且末尾不是4,现在添加4,返回1状态
            newStatus = 1;
        else if (status == 1 && i != 4 && i != 9) // 高位不含49且末尾是4,现在添加的不是4和9,返回0状态
            newStatus = 0;
        else if (status == 1 && i == 9) // 高位不含49且末尾是4,现在添加9,返回2状态
            newStatus = 2;
        ans += dfs(pos - 1, newStatus, limit && i == up);
    }
    if (!limit) {
        dp[pos][status] = ans;
    }
    return ans;
}

int cal(ll n) {
    int cnt = 0;
    while (n) {
        digit[++cnt] = n % 10;
        n /= 10;
    }
    return cnt;
}

int main() { 
    int t;
    cin >> t;
    while (t--) {
        memset(dp, 0, sizeof(dp));
        cin >> n;
        cout << dfs(cal(n), 0, true) << endl;
    }
    return 0; 
}
posted @ 2021-01-19 22:38  ZZHHOOUU  阅读(157)  评论(0编辑  收藏  举报