数位DP

我最近看的是一个数位DP,这个类型的我看了好几个,有几个博客都不大好理解,最后才找到一个好理解的。

具体 举个例子:求【L,R】有多少个数里面含有5,或23

数组是一个二维的,第二维存着三个状态

dp[i][0]代表到一个i位数有多少个不存在上述条件的

dp[i][[1]代表一个i位数在不存在上述条件的前提下,最高位是3的个数

dp[i][2]代表一个i位数满足条件的个数

代码如下:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

int dp[10][3];  

void init()
{
    memset(dp, 0, sizeof(dp));
    dp[0][0] = 1;  
    for(int i = 1; i <= 9; i ++)
    {
        dp[i][0] = dp[i - 1][0] * 9 - dp[i - 1][1];    //  在第一种情况下最高位添加除5之外的数,但是上一位是3,最高位加上了2,就不满足这个条件了
        //这个地方我想了好久,因为没有注意是第一种情况包含第二种情况,反正想了好久

        dp[i][1] = dp[i - 1][0];          //在第一种情况下,在最高位添加3的个数
        dp[i][2] = dp[i - 1][2] * 10 + dp[i - 1][0] + dp[i - 1][1];   
		//在满足条件的情况下在最高位加10种任意数都满足和在第一种情况下前面加5和第二种条件下加上2
       }
}


int solve(int x)
{
    int digit[15];
    int cnt = 0;
    int tmp = x;
    while(tmp)             //   取出每一位的数,把整数分为各个数位
    {
        digit[++cnt] = tmp % 10;
        tmp /= 10;
    }
    digit[cnt + 1] = 0;  // 为了下面的digit[i + 1],i = cnt 的时候
    int flag = 0, ans = 0;              // ans 代表符合题意的数的个数
    for(int i = cnt; i > 0; i --)
    {
        ans += digit[i] * dp[i - 1][2];
        if(flag)    // 当此数满足题意时,比如这个数是 9236
            ans += digit[i] * dp[i - 1][0];   //  所以 923之间满足题意的 , 再乘以6 就是所有满足题意的数的个数
        else
        {
            if(digit[i] > 5)    //  当你这个位置上的数大于5 的时候,说明你这个数肯定包含了这个位置存在5 的情况
                ans += dp[i - 1][0];
            if(digit[i] > 2)
                ans += dp[i - 1][1];
            if(digit[i + 1] == 2 && digit[i] > 3)
                ans += dp[i][1];
        }
        if(digit[i] == 5 || digit[i + 1] == 2 && digit[i] == 3)
            flag = 1;
    }
    return ans;
}

int main()
{
    int a, b;
    init();
    while(~scanf("%d%d",&a,&b))
    {
        if(a == 0 && b == 0)
            break;
        printf("%d\n", solve(b + 1) - solve(a));   //  在sovle 函数中,解决没有包括b ,所以要b + 1(这个地方我没有理解,还要再看看)
    }
}


(我的理解也在里面了,就是代码粘的别的大佬的http://m.blog.youkuaiyun.com/xi__long/article/details/38091561)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值