题目链接: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;
}