数位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,以后诸如此类问题要记录已经组成的数中出现了多少题目要求的数)

posted @ 2018-09-20 17:38  Splitor  阅读(125)  评论(0编辑  收藏  举报