• 浅谈数位dp——hdu 不要62


                                                                                                                 HDU-------不要62

                                                                                            题目传送门

                                                    Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

    问题描述:

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

    输入:

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

    输出:

    对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。
    样列输入:
    1 100
    0 0
    样列输出:
    80
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    题目解释:
    题目是中文题目,因此应该比较好理解。意思是给你两个数字(n和m,n<m),让你求出从n到m中不包含62和4的数字,这个范围也包含
    n和m。
    当我们看到这个题目的第一个想法:既然是数位,只要把从1到n和m的数字全部便利一边并做一个检查,检查方法为:对10取余判断是否是否存在位数
    4和对100取余判断是否存在62,直接暴力就可以过
    #include<stdio.h>
     
    int num[1000000 + 10] = {0}; //打表记录 1 到 1000000 前i个数中吉利的数字有多少个
    int n, m;
     
    int check(int x)
    {
        while(x)
        {
            if(x % 10 == 4 || x % 100 == 62) //不吉利
                return 0;
            x /= 10; //每位都要判断
        }
        return 1; //非不吉利数 返回 1, 贡献+1
    }
     
    int main()
    {
        for(int i = 1; i <= 1000000; i ++)
            num[i] = num[i - 1] + check(i); //前缀和思想, 前i个非不吉利数为前 i - 1个已经记录的 + check(i) 的贡献
        while(scanf("%d%d", &n, &m)!=EOF)
        {
            if(n == 0 && m == 0)
                break;
            printf("%d
    ", num[m] - num[n - 1]);
        }
        return 0;
    }

    大多数的暴力方法都受数据范围的限制,当n或者m很大的时候就很容易出现超时。因此需要一个更加优良的方法————数位dp

        数字与它每一位的关系, 知道一个数字n, 我们便可以通过对它 进行 对10取模再整除, 可以依次得到各个位置上的数,以及n的长度len,用数位dp的话我们需要用到 数的长度 以及 各个位置上的数值

    处理方法如下

     1 int length(int n)
     2 {
     3     int len=0;
     4     while(n)
     5     {
     6         len++;
     7         n/=10;
     8     }
     9     return len;
    10 }   // 该数值的长度
    void caldigit(int n,int len)
    {
        memset(digit,0,sizeof(digit));
        for(int i=1;i<=len;i++)
        {
            digit[i]=n%10;
            n/=10;
        }
    }    // 该数值各个位的数

    简单了解一下数位dp,数位DP一般应用于求出在给定区间[A,B]内,符合条件P(i)的数i的个数,条件P(i)一般与数的大小无关,而与数的组成有关。

    因此我们需要需要一个二维数组dp[i][j]表示以j为最高位的i位数,看某位大佬举得例子
    例如count(324)结果为 dp[3][0] + dp[3][1] + dp[3][2] + dp[2][0] + dp[2][1] + dp[1][0] + dp[1][1] + dp[1][2] + dp[1][3]
    
    这里要说明一下,认为009是长度为3,首位为0的符合条件的数,09是长度为2首位为0符合条件的数
    
    在上面的代码中展示了 我们用digit[]数组记录了n的各个位数上的值, 也就是说从高位往地位处理才是正确的方法, 这样可以优化, 当高位为4或者62时便可以停止count了, 不再计算低位的dp值
    
    例如3456,我们先是dp[4][1]即 3000~3999
    
    然后加上dp[4][2] + dp[4][3](不再列举, 参考上面) + dp[3][1] + ... +当加到dp[3][3]时我们就应该停止计算了
    
    因为digit[3] == 4,接下来计算的值将是401~456可以知道这都包含4,全为不吉利数字,停止往地位继续计算,同理自己要去想想62的情况,在草稿纸上模拟一下就很容易理解了

    除此之位可以对数组dp进性预处理,这样在查询的时候可以直接访问

    ac代码如下

    #include<stdio.h>
    #include<string.h>
    int digit[10];
    int dp[10][10];
    int length(int n)
    {
        int len=0;
        while(n)
        {
            len++;
            n/=10;
        }
        return len;
    }   // 该数值的长度
    void caldigit(int n,int len)
    {
        memset(digit,0,sizeof(digit));
        for(int i=1;i<=len;i++)
        {
            digit[i]=n%10;
            n/=10;
        }
    }    // 该数值各个位的数
    int count(int n)
    {
        int ans=0;
        int len=length(n);
        caldigit(n,len);
        for(int i=len;i>=1;i--)//从高位开始
        {
            for(int j=0;j<digit[i];j++)//枚举该位上的每一位数字
            {
                if(j!=4&&!(j==2&&digit[i+1]==6))
                ans+=dp[i][j];
            }
            if(digit[i]==4||(digit[i]==2&&digit[i+1]==6))//倘若该位上为62或者4,就不必继续枚举,因为后面的数一定不符合,例如452,453,624
            break;
        }
        return ans;
    }
    int main()
    {
        dp[0][0]=1;
        for(int i=1;i<=7;i++)//对dp做预处理
        {
            for(int j=0;j<10;j++) //第i位上的数
            {
                for(int k=0;k<10;k++)//第 i - 1位上的数
                {
                    if(!(j==4)&&!(j==6&&k==2)) //不为4或者62才能算一个贡献
                    dp[i][j]+=dp[i-1][k];
                }
            }
        }     
        int n,m;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            if(n==0&&m==0)
            break;
            printf("%d
    ",count(m+1)-count(n));
        }
        return 0;
    }

    第三种方法就是数位dp plus 记忆化收索

    dp数组的生明也是二维的,只不过表示的意义有所不同,以hdu不要92为列,dp[i][2]存的是第i位上的吉利数的个数,0表示这个位置上不是6,1表示这个位置上是6,

    因为是6和不是6是两种情况,同时也需要分开储存,,这种方法也需要对数据进性分解处理,只不过是初始的时候初始为0罢了,代码很简单也只有短短的几行,只不过需要

    较长时间的理解

    #include<iostream>
    #include<string.h>
    #define maxn 100000+10
    using namespace std;
    int dp[20][2],shu[20];
    int dfs(int len,bool if6,bool shangxian)
    {
        if(len==0)  return 1;
        if(!shangxian&&dp[len][if6])  return dp[len][if6];
        int cnt=0;
        int maxx=shangxian?shu[len]:9;
        for(int i=0;i<=maxx;i++)
        {
            if(i==4||if6&&i==2)  continue;
            cnt+=dfs(len-1,i==6,shangxian&&i==maxx);
        }
        return shangxian?cnt:dp[len][if6]=cnt;
    }
    int solve(int k)
    {
        int cnt=0;
        memset(shu,0,sizeof(shu));
        while(k)
        {
            shu[++cnt]=k%10;
            k=k/10;
        }
        return     dfs(cnt,false,true);
    }
    int main()
    {
        int n,m;
        while(cin>>n>>m)
        {
            if(n==0&&m==0)  break;
            memset(dp,0,sizeof(dp));
            printf("%d
    ",solve(m)-solve(n-1));
        }
        return 0;
    } 
  • 相关阅读:
    laydate日期插件弹出闪退和多次闪退问题解决
    IconFont 图标的3种引用方式
    jquery操作select下拉框的多种方法(选中,取值,赋值等)
    jquery获取select选中项 自定义属性的值
    Centos查看端口占用、开启端口
    IaaS,PaaS,SaaS 的区别
    IOS手机使用Fiddler抓包(HTTPS)(二)
    Android手机使用Fiddler抓包(HTTPS)(二)
    Fiddler使用详解(一)
    python2.x和python3.x中raw_input( )和input( )区别
  • 原文地址:https://www.cnblogs.com/tombraider-shadow/p/10596166.html
Copyright © 2020-2023  润新知