数位dp暂退-[ZJOI2010]数字计数
特殊的计数问题
这道题不同于之前,之前都是满足****条件的数有多少个,现在是统计某个小数字在所有的数字中出现了几次。
考虑暴力(以1为例子):
0->1 2 3 4 5 6 7 8 9
1->0 1 2 3 4 5 6 7 8 9
我们只要枚举每一位的每一种可能,然后记录组成的数中1出现了多少次,就可以了。
然后我们考虑用记忆化来优化:令f[i][j][0/1][0/1] 表示考虑到了第i位,在组起的数中统计的数已经出现了j次,后两位就是数位dp常见的前导零,上界的处理了。
这样到达同一种状态时就会返回。
code:
#include<cstdio>
#include<cstring>
#include<iostream>
#define int long long
using namespace std;
const int maxn = 15;
int f[maxn][2][maxn][2];
int num[maxn];
int dfs(int len, bool lmt, int cur, bool zeo, int d)
{
int ans = 0;
if (len == 0) return cur;
if (f[len][lmt][cur][zeo] != -1) return f[len][lmt][cur][zeo];
for (int i = 0; i < 10; i ++){
if (!lmt && i > num[len]) break;
ans += dfs(len-1, lmt || (i<num[len]), cur+((!zeo || i) && (i==d)), zeo && (i == 0), d);
}
f[len][lmt][cur][zeo] = ans;
return ans;
}
int solve(int x, int d)
{
int len = 0;
while (x){
num[++ len] = x%10;
x /= 10;
}
memset(f, -1, sizeof f);
return dfs(len, 0, 0, 1, d);
}
signed main()
{
int a, b;
cin>>a>>b;
for (int i = 0; i < 10; i ++)
printf("%lld%c", solve(b, i)-solve(a-1, i), i == 9 ? '\n' : ' ');
return 0;
}
总结:
又是一道典型的暴力+优化==正解 的问题,主要是思路不够宽广,应该先打出暴力,然后优化(主要是暴力我打的也不太对qwq,以后诸如此类问题要记录已经组成的数中出现了多少题目要求的数)