zoukankan      html  css  js  c++  java
  • hdu 2089 数位dp

    链接:https://vjudge.net/problem/23625/origin

    中文,题目不用说了。

    其实这题的数据很小,所以直接暴力也可以过,但是还是要学会数位dp,因为并不是每一题的数据都会这么小,这里给出我暴力的代码和数位dp的代码(数位dp其实也是有一个大致的模板的)。

    暴力:

    #include<stdio.h>
    #include<string.h>
    int dp[1000005];
    int n,m,k,t;
    int jug(int a)
    {
        while(a)
        {
            if(a%10==4)
            return 0;
            if(a%10==2&&a/10%10==6&&a>=62)
            return 0;
            a/=10;
        }
        return 1;
    }
    int main()
    {
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=1000000;i++)
        {
            if(jug(i))
            dp[i]=dp[i-1]+1;
            else
            dp[i]=dp[i-1];
        }
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            if(!n&&!m)
            break;
            if(m<n)
            printf("%d
    ",0);
            else
            printf("%d
    ",dp[m]-dp[n-1]);
        }
        return 0;
     } 

    接下来我只是说一下我的看法,可能不是非常准确,能大致看懂就好。现在假设我们求0到625符合条件的数字有多少个,那么我们把625的每一位存进一个digit数组,先介绍一下我们递归时的顺序(值始终不能超过625),先把百位拿出来,因为百位为6,那么我们可以这样找

    先找百位,百位的上界为6,所以百位是受到约束的,最大只能取6,所以百位可以取0,1,2,3,4,5,6。

    接下来找十位,0_ _,1_ _,2_ _,3_ _,4_ _,5_ _,前面的这些它们的十位不受约束可以随便取(比如00_,01_,... 09_,  10_,11_...19_  ... 59_),从0到9都可以。

    但是到了6(6_ _)就要控制后面的数字了,因为它的十位最大只能取2,所以我们只能取6 0 _,6 1_,6 2 _,同样,在我们递归到了十位时,如果百位是0到5,那么它们的个位也是不受约束的,所以也可以随便取,

    但是如果百位是6,那么我们又要看情况了,如果是60_,或者是61_,那么它们的个位也是不受约束的,可以随便取,但是如果是62_,那么它的个位是受到约束的,最大只能到5。

    应该差不多了,在程序里,我们从最高位一直递归到个位,同时把当前位不受约束的状态的值保存下来,pos表示现在到了

    哪一位(百位?十位?个位?),flag表示到到当前状态为止是否符合条件(0或1),pre表示当前状态的前一位数字是多少(判断是不是有62),limit就是控制上界的关键,我们把它初始化为1,表示目前它的最高位收到限制,在之后的过程中

    如果limit为0,那么接下来所有的递归过程里数字都是0到9,都可以取,都可以记忆保存,可能看文字不是特别明白,

    还是看看代码吧:

    #include<stdio.h>
    #include<string.h>
    int dp[10][15][5];
    int digit[15];
    int n,m,k,t;
    void init()
    {
        memset(dp,-1,sizeof(dp));
    }
    int dfs(int pos,int flag,int pre,int limit)//当前位,是否满足条件,前一位,控制上界 
    {
        if(!pos)//到了个位,返回值 
        return flag;
        if(!limit&&dp[pos][pre][flag]!=-1)//直接输出之前记录的值,节省时间 
        return dp[pos][pre][flag];
        int num=limit?digit[pos]:9;//非常关键的东西,如果limit为1,那么这个位置受到约束,最大只能为digit[pos] 
        int s=0;                    //否则它就不受约束,可以为任何值 
        for(int i=0;i<=num;i++)
        {
            if(pre==6&&i==2||!flag||i==4)
            s+=dfs(pos-1,0,i,limit&&i==num);//这个limit&&i==num是为了确定下一位数是否受约束 
            else
            s+=dfs(pos-1,1,i,limit&&i==num);
        }
        if(!limit&&dp[pos][pre][flag]==-1)//记录 
        dp[pos][pre][flag]=s;
        return s;
        
    }
    int cal(int  a)
    {
        int len=0;
        memset(digit,0,sizeof(digit));
        while(a)
        {
            digit[++len]=a%10;
            a/=10;
        }
        return dfs(len,1,0,1);
    }
    int main()
    {
        init();
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            if(!n&&!m)
            break;
            printf("%d
    ",cal(m)-cal(n-1));
        }
        return n;
     } 
  • 相关阅读:
    7款强大的Javascript网格插件推荐 狼人:
    90后英国中学生建立黑客社交网 涉案金额达1.8亿元 狼人:
    好的代码里只要一个return语句 狼人:
    一个月内从零开始做webOS开发人员 狼人:
    FireFox 5开发计划曝光 内嵌PDF阅读器(组图) 狼人:
    谷歌用户体验设计准则 狼人:
    15个编程好习惯 狼人:
    Debain/ArchLinux/Gentoo 等将合并为超级Linux 狼人:
    别说你不知IE9正式版浏览器小技巧9则 狼人:
    Firebug1.8a1发布 新功能、新架构 狼人:
  • 原文地址:https://www.cnblogs.com/6262369sss/p/8969800.html
Copyright © 2011-2022 走看看