• HDU 2089 不要62(数位DP)


    题目:

    杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。 
    杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。 
    不吉利的数字为所有含有4或62的号码。例如: 
    62315 73418 88914 
    都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。 
    你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。 

    Inpu

    t输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。 


    Output

    对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。 

    Sample Input

    1 100
    0 0

    Sample Output

    80

    题意:给出2个数n,m 且 n<=m,问区间【n,m】中有多少个不包含4和不包含连续的62的数。
    暴力枚举就超时了,下面介绍一下数位DP

    数位DP也就是一个记忆化搜索的过程,其实也就是模拟数数的过程(深搜)
    举个栗子,我们正常从1到5123数数
    就相当于数0001,0002,.....0010,0011,0012......1000....5123这样一个一个数
    而数位DP,就是按个位,十位,百位,按位来数
    思想是这样的,假设
    n=1,m=5123

    上面划线的数字组合起来就表示5123

    我们定义,dp【20】【2】

         digit【20】//把数字的每一个数位存起来,对5123来说,digit【1】=3,digit【2】=2,digit【3】=1,digit【4】=5,

    解释:dp【i】【1】表示当数位i+1的数字是6时候,数位i有多少个不包含4和不包含62的数

          dp【i】【0】表示当数位i+1的数字不是6时候,数位i有多少个不包含4和不包含62的数

    比如dp【1】【0】= 9(当数位2,即十位的数字不是6时,个位可以提供的数字有0,1,2,3,5,6,7,8,9共9个)

      dp【1】【1】= 8(当数位2,即十位的数字是6是,个位可以提供的数字有0,1,3,5,6,7,8,9共8个)

    我们先跑一遍位数是1的数,更新dp【1】【1】和dp【1】【0】

    当我们跑到十位0时,因为这时候的数字是0,不是6,所以十位数是0的贡献是dp【1】【0】=9

    同理,当十位是1,2,3,5,7,8,9是,贡献都是dp【1】【0】=9

    十位是4的时候,因为不能出现数字4,所以它的贡献是0

    十位是6的时候,贡献是dp【1】【1】=8(60,61,63,65,66,67,68,69共8个)

    更新一下dp【2】【0】

    它的值就应该是十位数字0,1,2,3,5,6,7,8,9贡献之和,即9*8+8=80

    再更新一下dp【2】【1】

    它的值就应该是十位数字0,1,3,5,6,7,8,9贡献之和,即9*7+8=71

    这样就可以一直更新上去

    把各个数位上的数字贡献记录下来,提供给更高位的数位,这样就实现了记忆化搜索

    当然还有边界问题

    当我们千位是5的时候,千位(最高位)的数字到了最大值,百位就只能看0和1的贡献

    百位数字为0的贡献可以直接利用,因为5000-5099都在1-5125的范围内

    但1是百位的最大值(在千位是最高位的前提下),不能直接利用百位是1的贡献(5100-5199有部分数超过了5123)

    所以十位是0,1的贡献可以直接利用,十位是2的贡献不能直接利用

    只需要特判一下就可以了

    这个过程还是听视频讲解的好(画图要画好多张啊,偷懒偷懒)

    推荐一个视频  https://www.bilibili.com/video/av27156563?t=2383

    博主也是看了这位大佬的视频才理解了数位dp(当然下面的代码也是这位大佬的)

    代码实现:(详细看注释)

    ll dfs(int len,bool if6,bool limit)
    {/*len表示当前的数位,if6表示上一个数位的数字是不是6,limit表示比现在数位高的数位是不是都到了最大值,即限制值,如千位是5,百位是1,那么十位就只能取0,1,2,起到了限制作用*/
        if(len==0) return 1;//递归边界
        if(!limit && dp[len][if6]) return dp[len][if6];
    /*提速部分,如果没有被限制而且dp数组已经更新,则直接使用dp数组*/
        ll cnt=0,up_bound=(limit?digit[len]:9);
    /*cnt用来计数,up_bound表示上界,如果高位没有被限制,则该位可以是数字0到9,否则该位数字是0到digit【len】,对于十位来说就是0到2(假如千位是5,百位是1,高位限制了)*/
        for(int i=0;i<=up_bound;++i){
            if(if6 && i==2) continue;//上一位数字是6而且当前位数数字是2,忽略
            if(i==4) continue;//当前数位数字是4,忽略
            cnt+=dfs(len-1,i==6,limit&&i==up_bound);
    /*下一个数位的各个数字贡献之和就是当前位数数字是i的贡献,第三个参数表示当前数位和高数位是不是都达到了限制值*/
        }
        if(!limit) dp[len][if6]=cnt;
    /*没有被限制的数位计算出的cnt才能代表整个数位的贡献,否则会有残缺*/
        return cnt;
    }
    ll solve(ll num)
    {
        int k=0;
        while(num)//分解num各个数位上的数字
        {
            digit[++k]=num%10;
            num/=10;
        }
        return dfs(k,0,1);
    /*从高位开始向低位搜索,最高的数自然是自己本身,它的上一位只能是0,不是6,所以第二个参数写0,因为没有15123,只有05123,所以第三个参数写1*/
    }
    View Code

    AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long int ll;
    int digit[20];
    ll dp[20][2];
    ll dfs(int len,bool if6,bool limit)
    {
        if(len==0) return 1;
        if(!limit && dp[len][if6]) return dp[len][if6];
        ll cnt=0,up_bound=(limit?digit[len]:9);//上界;
        for(int i=0;i<=up_bound;++i){
            if(if6 && i==2) continue;
            if(i==4) continue;
            cnt+=dfs(len-1,i==6,limit&&i==up_bound);
        }
        if(!limit) dp[len][if6]=cnt;
        return cnt;
    }
    ll solve(ll num)
    {
        int k=0;
        while(num)
        {
            digit[++k]=num%10;
            num/=10;
        }
        return dfs(k,0,1);
    }
    int main()
    {
        ll n,m;
        while(cin>>n>>m)
        {
            memset(dp,0,sizeof(dp));
            if(n==0&&m==0) break;
            cout<<solve(m)-solve(n-1)<<'
    ';
        }
        return 0;
    }
    View Code
     
  • 相关阅读:
    java23种设计模式(五)--组合模式
    elasticsearch删除
    Jedis
    Redis主从复制(含哨兵模式)
    Redis持久化
    Redis基本知识(含数据类型)
    Linux学习(含有常用命令集)
    深入Kafka
    Kafka消费者
    Kafka生产者
  • 原文地址:https://www.cnblogs.com/Remilia-Scarlet/p/10873088.html
Copyright © 2020-2023  润新知