HDU 2089 不要62(数位dp)

题目链接:

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

题解:

开一个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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值