数位dp

数位dp
数位dp是一种计数用的dp,一般就是要统计一个区间[le,ri]内满足一些条件数的个数。所谓数位dp,字面意思就是在数位上进行dp咯。数位还算是比较好听的名字,数位的含义:一个数有个位、十位、百位、千位…数的每一位就是数位啦!

之所以要引入数位的概念完全就是为了dp。数位dp的实质就是换一种暴力枚举的方式,使得新的枚举方式满足dp的性质,然后记忆化就可以了。

新的枚举:控制上界枚举,从最高位开始往下枚举,例如:ri=213,那么我们从百位开始枚举:百位可能的情况有0,1,2(觉得这里枚举0有问题的继续看)

然后每一位枚举都不能让枚举的这个数超过上界213(下界就是0或者1,这个次要),当百位枚举了1,那么十位枚举就是从0到9,因为百位1已经比上界2小了,后面数位枚举什么都不可能超过上界。所以问题就在于:当高位枚举刚好达到上界是,那么紧接着的一位枚举就有上界限制了。具体的这里如果百位枚举了2,那么十位的枚举情况就是0到1,如果前两位枚举了21,最后一位之是0到3(这一点正好对于代码模板里的一个变量limit 专门用来判断枚举范围)。最后一个问题:最高位枚举0:百位枚举0,相当于此时我枚举的这个数最多是两位数,如果十位继续枚举0,那么我枚举的就是以为数咯,因为我们要枚举的是小于等于ri的所以数,当然不能少了位数比ri小的咯!(这样枚举是为了无遗漏的枚举,不过可能会带来一个问题,就是前导零的问题,模板里用lead变量表示,不过这个不是每个题目都是会有影响的,可能前导零不会影响我们计数,具体要看题目)

不要62
题目: 戳我
模板:

#include <iostream>

using namespace std;
//不要62
int dp[20][10];
int a[20];
int dfs(int pos,int sta,bool limit)//sta 为上一位是否为6标记 limit为边界标记
{
    if(pos==-1)
        return 1;
    if(!limit && dp[pos][sta]!=-1)
        return dp[pos][sta];
    int up=limit?a[pos]:9;//边界限制 使其数不会大于其边界值
    int sum=0;
    for(int i=0;i<=up;i++)
    {
        if(i==4 ||(sta&&i==2))
            continue;
        sum+=dfs(pos-1,i==6,limit&&i==up);
    }
    if(!limit)//当为边界时 其结果不具备参考性所以不能将其加入dp中
        dp[pos][sta]=sum;
    return sum;
}
int main()
{
    int n,m;
    for(int i=0;i<20;i++)
        for(int j=0;j<10;j++)
            dp[i][j]=-1;
    while(cin>>n>>m)
    {
        if(n==0 && m==0)
            break;
        int cnt=0;
        while(m)
        {
            a[cnt++]=m%10;
            m/=10;
        }
        int ans1=dfs(cnt-1,0,true);
        cnt=0;
        n--;
        while(n)
        {
            a[cnt++]=n%10;
            n/=10;
        }
        int ans2=dfs(cnt-1,0,true);
        cout<<ans1-ans2<<endl;
        //cout<<dfs(m,0,0,true)-dfs(n,0,0,true)<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值