Hdu3555.Bomb

3555.Bomb

题目描述

The counter-terrorists found a time bomb in the dust. But this time the terrorists improve on the time bomb. The number sequence of the time bomb counts from 1 to N. If the current number sequence includes the sub-sequence "49", the power of the blast would add one point.
Now the counter-terrorist knows the number N. They want to know the final points of the power. Can you help them?
大意:求 \(1\)\(N\) 有多少个数字含“49” 。

输入描述

The first line of input consists of an integer T (1 ⇐ T ⇐ 10000), indicating the number of test cases. For each test case, there will be an integer N (1 ⇐ N ⇐ 2^63-1) as the description.

The input terminates by end of file marker.
大意:第一行输入一个$T$(\(1\leq T \leq 10000\))表示测试组数,接下来 \(T\) 行每行输入一个 \(N\)(\(1\leq N \leq 2^{63}-1\))表示右边界。

输出描述

For each test case, output an integer indicating the final points of the power.
大意:对于每组测试,输出一个整数表示最后的答案。

解题思路

分析发现数据范围到了long long级别,显然暴力是不可行的,本题是求某一区间内符合某种特征的数字数量,因此可以考虑数位DP,本题也正是一道经典的数位DP入门题,对于数位DP一般有两种做法,一种是像常规DP一样根据转移方程不断推进,另一种则是记忆化搜索,相对而言,记忆化搜索的方法更加套路一些,更容易调试。
先开一个数组digit,用于存放数字(注意要反向存入,便于直接应用最高位的限制条件,同时也可以方便地处理前导零的问题),再开一个dp数组,用于存放中间过程的答案。接下来考虑dfs的传参设置,简单来说要考虑的有三点,当前位置pos,前一个位置pre,当前位置选用的数字是否要受限制(不可以超出范围)。然后对于根据dp数组和dfs参数的状态进行判断选择不同的分支不断递归即可。

代码实现

#include <bits/stdc++.h>

using i64 = long long;

i64 digit[20], dp[20][10];  // 数组大小20是因为10^20 > 2^63-1,dp数组的第二位10则用于和pos的前一个位置比较,一个位置上的数字%10之后不会大于9,因此开10即可。

i64 dfs(int pos, int pre, bool limit) {
    if (pos == 0) {  // 如果已经处理完所有位,则返回1,表示找到一个满足条件的数字。
        return 1;
    }
    if (!limit && dp[pos][pre] != 0) {  // 如果不受限且已经计算过该状态,则直接返回结果,避免重复计算。
        return dp[pos][pre];
    }
    i64 maxn = limit ? digit[pos] : 9, res = 0;  // 如果受限,则最大值为当前位的数值;否则为9。
    for (int i = 0; i <= maxn; i++) {
        if (pre == 4 && i == 9) {  // 如果前一位是4且当前位是9,则跳过这种情况,因为不满足条件。
            continue;
        }
        // 递归处理下一位,注意条件limit && i == digit[pos],limit是当前位数的限制,i == digit[pos]是对下一位的限制,例如520,当前位(百位)是5的时候才需要考虑下一位是不是应该不超过2。
        res += dfs(pos - 1, i, limit && i == digit[pos]);
    }
    if (!limit) {  // 不受限制则使用记忆化
        dp[pos][pre] = res;
    }
    return res;
}

i64 calc(i64 x) {
    int pos = 0;
    while (x > 0) {
        digit[++pos] = x % 10;
        x /= 10;
    }
    return dfs(pos, 0, true);
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    int t;
    std::cin >> t;
    while (t--) {
        i64 x;
        std::cin >> x;
        std::cout << x - calc(x) + 1 << "\n";
    }
}
posted @ 2024-08-05 19:58  udiandianis  阅读(85)  评论(0)    收藏  举报