昨天做了一道很奇怪的数字题,不知道怎么做,今天才知道是数位DP ……我来学习学习。
传送门
大意:给定区间[n,m],求在n到m中没有“62“或“4“的数的个数。如62315包含62,88914包含4,这两个数都是不合法的。

思路:做这道题我们就要用到数位DP了,我们可以线预处理出一个F数组。用F[i,j]代表开头是j的i位数中不含”62”或”4”的数有几个。这样我们很好写出一个状态转移方程F[i,j]=F[i1,k]|j4j=6k2。我们做的时候,从高到低枚举哪一位比n的哪一位小(这是可以为0的,我们枚举的时候不能取等就是因为我们算的是比他小的。比如说我们找一个数52496:首先答案会加上f[5][3]+f[5][2]+f[5][1]+f[5][0]+f[4][1]+f[4][0]然后就不加了,分别表示三开头的五位数,二开头的五位数,一开头的五位数,任意位数非5位数,以五一开头的五位数,以五零开头的五位数)是不是很易懂呢?

上代码:

#include<cstdio>
#include<cstring>
int f[10][10], n, m;
int bit[10];
int dp(int len)
{
    int ans = 0;
    bit[len + 1] = 0;
    for(int i = len; i > 0; i --){
        for(int j = 0; j < bit[i]; j ++){
            if(j == 2 && bit[i+1] == 6) continue;
            ans += f[i][j];
        }
        if(bit[i] == 4||(bit[i] == 2 && bit[i+1] == 6))    //找到了4或者连续的62说明后面的数都是接在4和62后面的,所以直接退出
            break;
    }
    return ans;
}
int main()
{
    f[0][0] = 1;
    for(int i = 1; i <= 7; i ++)
        for(int j = 0; j <= 9; j ++)
        {
            if(j == 4) continue;
            for(int k = 0; k <= 9; k ++)
            {
                if((j == 6 && k == 2) || k == 4) continue;
                f[i][j] += f[i-1][k];
            }
        }
    while(~scanf("%d%d", &n, &m) && n + m) {
        m ++;                 //因为我们只算了[1,r)的,所以上去间 ++
        int t1 = n, t2 = m, l1 = 0, l2 = 0;
        while(t1)
        {
            l1 ++;
            bit[l1] = t1 % 10;
            t1 /= 10;
        }
        t1 = dp(l1);
        while(t2)
        {
            l2 ++;
            bit[l2] = t2 % 10;
            t2 /= 10;
        }
        t2 = dp(l2);
        printf("%d\n", t2 - t1);
    }
    return 0;
}
posted on 2015-08-24 20:48  geng4512  阅读(128)  评论(0编辑  收藏  举报