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";
}
}
标签:std,10,3555,Bomb,int,pos,i64,dp
From: https://www.cnblogs.com/udiandianis/p/18343794