HDU 2089 不要62(数位dp)

写在前面

这条博客的目的是完成作业,就搬运了自己以前的一篇博客略加修改,内容是自己以前写的一道acm题,应该也算project吧......

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=2089

题目大意

给出一个数字区间n,m,求出这个区间内有多少数字不含有4和62.

题解

开一个dp数组,设i代表当前数字的长度,dp[i][0]表示不含4和62的数字, dp[i][1] 表示不含4和62且最高位为2的数字,dp[i][2]表示含有4和62的数字。可以推出以下的递推公式:

dp[0][0] = 1;
    for(i = 1; i<=6; i++)//数字最长为6
    {
        dp[i][0] = dp[i-1][0]*9-dp[i-1][1];//最高位加上不含4的9个数字的状况,但因为会放6,所以要减去前一种开头为2的情况
        dp[i][1] = dp[i-1][0];//开头只放了2
        dp[i][2] = dp[i-1][2]*10+dp[i-1][0]+dp[i-1][1];//已经含有的前面放什么数都可以,或者是放一个4,或者是在2前面放6
    }

然后按位读入数字,从最高位开始读数字判断,设flag为之前的高位是否出现过4或者62,ans为不吉利数字的数量。

开始先将第i位数字与前一位含有4和62的数字的乘积加入ans(用1到74举例,当遍历到7时加入的是14,24,34,44,54,64,74,遍历到4时加入的数字是4),然后分情况讨论:

1.如果flag为真,说明已经是不吉利数字了,那么接下来怎么处理已经无所谓了,所以加上该位数字与前一位数字不含4和62的数字的乘积。

2.如果flag不为真且当前位数字大于4,代表可以将以4开头的数字加入ans,所以ans+=number[i] * dp[i-1][0](还是1到74为例,遍历到7时加入的数字是40 ,41,42,43,45,46,47,48,49)

3.如果flag不为真且当前位数字大于6,代表可以加入62(以1到74为例,此时加入了62)

4.如果flag不为真且后一位数字等于6且当前位数字大于2,代表可以加入62(加入62的第二种情况)

总结:按照1到74的例子来说,遍历结束后ans为18,74 - 18即是我们需要的值。

AC代码

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int n, m;
int dp[7][3];

int solve(int number)//处理的数字不包含number本身
{
    int numb[8] = {0}, len = 0, ans = 0,cnt = number;
    while(number != 0)
    {
        numb[++len] = number % 10;
        number /= 10;
    }
    bool flag = false;
    for(int i = len; i > 0; i--)
    {
        ans += numb[i] * dp[i-1][2];
        if(flag)
            ans += numb[i] * dp[i-1][0];
        if(!flag && numb[i] > 4)
            ans += dp[i-1][0];
        if(!flag && numb[i+1] == 6 && numb[i] > 2)
            ans += dp[i][1];
        if(!flag && numb[i] > 6)
            ans += dp[i-1][1];
        if((numb[i] == 2 && numb[i+1] == 6)|| numb[i] == 4)
            flag = true;
    }
    return cnt - ans;
}
int main()
{
    dp[0][0] = 1;
    for(int i = 1; i < 7; 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][0] + dp[i-1][1] + dp[i-1][2] * 10;
    }
    while(cin >> n >> m)
    {
        if(n ==0 &&  m == 0)break;
        cout << solve(m + 1) - solve(n) <<endl;
    }
    return 0;
}

当初遇到的问题

当时少考虑了一种情况,如果flag为之前的高位是否出现过4或者62,我少考虑了如果flag不为真且后一位数字等于6且当前位数字大于2,代表可以加入62,导致最后总的数字多了一些。当时通过一些sample样例,但是提交到HDU上时返回错误,最后通过手算一些大样例发现了错误所在,并且成功推导出了错误,最终提交通过

posted @ 2018-03-12 14:47  Iridescent_Izayoi  阅读(146)  评论(0编辑  收藏  举报