HDU2089 不要62

中文题意自然不用赘述,原题请戳

解题思路

做的第一道数位DP的题,果然数位DP里面的各种思想都是我曾经没有怎么接触过的。
首先我们将状态分为三种
dp[i][0]:表示从0到长度为i的数的区间里面不包含不吉利的数,且末位不是6的数的个数
dp[i][1]:表示从0到长度为i的数的区间里面不包含不吉利的数,且末位是6的数的个数
dp[i][2]:表示从0到长度为i的数的区间里面不吉利的数的个数。
这样的话我们很容易就能将dp数组预处理出来
dp[i][0]是由前i-1位里面所有的吉利数加上一个不是4的数,还要减去在6后面加上一个2的情况,所以dp[i][0]=dp[i-1][0]*9-dp[i][1]。
dp[i][1]是由前i-1位里面所有的吉利数加上一个6得到;
dp[i][2]是由前i-1位里面所有的不吉利数,加上前i-1位里面所有吉利数加上一个4,以及末位为6的数后面加上一个2得到。
然后这样dp数组我们就预处理出来了,但是现在怎么算从0到一个数x里面有多少个不吉利数呢?
首先对于x,我们将它的每一位分离出来,对于第i位暂且称其为si,对于这一位我们能得到的不吉利数的个数,首先是比它低的位数里面所有数里面的不吉利数乘以si,比如si=5,那么就应该是dp[i-1][2]×5,其实这样我们得到的就已经是0到50000(暂且算这么大吧)里面所有的不吉利数。然后如果这个si比4大,那么必然有比它位数低的所有的数加上一个4所构成的不吉利数,如果这个si大于6的话,同理。那么如果下一位是2,然后si比6大的话,那么就必然出现了62相连。如果此时此刻遍历过的数位里面已经满足了不吉利数的诸多性质,那么剩下的数位不管是什么,就都是不吉利数了。
第一次做数位DP,感觉这个比较新的思想做起来还是挺happy的。

AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int dp[10][3], a, b;
void init(){
    memset(dp, 0, sizeof(dp));
    dp[0][0] = 1;
    for (int i = 1; i <= 8; i++){
        dp[i][0] = dp[i-1][0] * 9 - dp[i-1][1];
        dp[i][1] = dp[i-1][0];
        dp[i][2] = dp[i-1][2]*10 + dp[i-1][1] + dp[i-1][0];
    }
}
int DP(int x){
    bool flag = 0;
    int s[15];
    int idx = 0, sum = x, ans = 0;
    for (; x; x /= 10)
        s[++idx] = (x%10);
    s[idx+1] = 0;
    for (int i = idx; i > 0; i--){
        ans += (dp[i-1][2]*s[i]);
        if (flag) ans += (dp[i-1][0]*s[i]);
        else{
            if (s[i] > 4) ans += dp[i-1][0];
            if (s[i] > 6) ans += dp[i-1][1];
            if (s[i+1] == 6 && s[i] > 2) ans += dp[i][1];
        }
        if (s[i] == 4 || s[i+1] == 6 && s[i] == 2) flag = 1;
    }
    return sum-ans;
}
int main(){
    init();
    while(scanf("%d%d", &a, &b) == 2 && (a+b))
        printf("%d\n", DP(b+1) - DP(a));
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值