zoukankan      html  css  js  c++  java
  • 数位DP入门

    以HDU2089为例:

    Problem Description
    杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。
    杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
    不吉利的数字为所有含有4或62的号码。例如:
    62315 73418 88914
    都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
    你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。
     
    Input
    输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。
     
    Output
    对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。
     
    Sample Input
    1 100 0 0
     
    Sample Output
    80
     
    Author
    qianneng

    即求从n-m中数位不含62和4的个数。

    设dp[i][j]表示长度为i,最高位是j的数位中符合条件的数

    显然 若j=4,dp[i][j]=0.若j=6,则dp[i][j]=sum(dp[i-1][k], k!=2,k>=0,k<9),否则dp[i][j]=sum(dp[i][k],k>=0,k<9)

    定义函数 count(x),表示严格小于x的数,满足条件的有多少个。

    则问题转化为求count(m+1)-count(n)。

    如何求count?

    考虑x的每一位,则分为两个部分,严格小于第i位的和加上第i位相等,比较第i-1位。

    比如 556 。就加上dp[3][1],dp[3][2],dp[3][3],dp[3][4],这是严格小于5的,然后等于5的部分,加上dp[2][1],dp[2][2],dp[2][3],dp[2][4],最后加上dp[1][1],dp[1][2],dp[1][3],dp[1][4],dp[1][5]。

    但有种特殊情况 如456. 先加上dp[3][1],dp[3][2],dp[3][3],这严格小于4的部分,之后到了等于4的部分,这是不满足条件的,所以不能算。直接break掉,62同理

    #include <bits/stdc++.h>
    #define maxn 25
    using namespace std;
    int dp[maxn][maxn];
    void init()
    {
        dp[0][0]=1;
        for(int i=1;i<=9;++i)
        {
            for(int j=0;j<=9;++j)
            {
                if(j==4) dp[i][j]=0;
                else if(j==6)
                {
                    for(int k=0;k<=9;++k)
                    {
                        if(k!=2) dp[i][j]+=dp[i-1][k];
                    }
                }
                else
                {
                    for(int k=0;k<=9;++k)
                    {
                         dp[i][j]+=dp[i-1][k];
                    }
                }
            }
        }
    }
    int a[10];
    int count(int x)
    {
        memset(a,0, sizeof(a));
        int cnt=0;
        while(x>0)
        {
            a[++cnt]=x%10;
            x/=10;
        }
        int res=0;
        for(int i=cnt;i>=1;--i)
        {
            for(int j=0;j<a[i];++j)
            {
                if(j!=4&&!(a[i+1]==6&&j==2)) res+=dp[i][j];
            }
            if(a[i]==4) break;
            if(a[i+1]==6&&a[i]==2) break;
        }
        return res;
    }
    int main()
    {
        int n,m,k;
        init();
        //cout<<dp[2][6]<<endl;
        while(~scanf("%d%d",&n,&m)&&n&&m)
        {
            printf("%d
    ",count(m+1)-count(n));
        }
        return 0;
    }
    

      计数位个数的感觉要麻烦许多

    [ZJOI2010]数字计数

    题目描述

    给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。

    输入输出格式

    输入格式:

    输入文件中仅包含一行两个整数a、b,含义如上所述。

    输出格式:

    输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。

    输入输出样例

    输入样例#1: 复制
    1 99
    
    输出样例#1: 复制
    9 20 20 20 20 20 20 20 20 20
    

    说明

    30%的数据中,a<=b<=10^6;

    100%的数据中,a<=b<=10^12。

    我们设dp[i][j][k]表示i位数,最高位为j拥有k的个数。

    则dp[i][j][k]=dp[i-1][l][k](0<=l<=9)+10^(i-1)。

    然后就可以数位dp了

    和上一个题一样,我们设count(x)能计算严格小于x的各数位的个数。

    首先小于x位数的,肯定都可以。我们直接加上。于是

        for(int i=cnt-1;i>=1;--i)
        {
            for(int j=1;j<=9;++j)
            {
                for(int k=0;k<=9;++k)
                {
                    ans[k]+=dp[i][j][k];
                }
            }
        }
    

      然后,比首位小的每一个数我们也可以加上

        for(int i=1;i<a[cnt];++i)
        {
            for(int k=0;k<=9;++k)
            {
                ans[k]+=dp[cnt][i][k];
            }
        }
    

      之后,我们从后往前枚举,第[i+1,cnt]位完全相同。则

        for(int i=cnt-1;i>=1;--i)
        {
            for(int j=0;j<a[i];++j)
            {
                for(int k=0;k<=9;++k)
                {
                    ans[k]+=dp[i][j][k];
                }
            }
            for(int j=cnt;j>i;--j)////2134  567
            {
                ans[a[j]]+=a[i]*cal(i-1);
            }
        }
    

     接下来是全部代码

    #include <bits/stdc++.h>
    typedef long long ll;
    using namespace std;
    ll dp[15][15][15];
    ll cal(ll n)
    {
        ll res=1;
        for(int i=1;i<=n;++i)
        {
            res=res*(ll)10;
        }
        return res;
    }
    void init()
    {
        for(int i=0;i<=9;++i)
        {
            dp[1][i][i]=1;
        }
        for(int i=2;i<15;++i)
        {
            for(int j=0;j<=9;++j)
            {
                for(int k=0;k<=9;++k)
                {
                    for(int l=0;l<=9;++l)
                    {
                        dp[i][j][k]+=dp[i-1][l][k];
                    }
                }
                dp[i][j][j]+=cal(i-1);
            }
        }
        //10 11 12 13 14 15 16 17 18 19
    }
    ll a[15];
    ll ans[15];
    void count(ll x)
    {
        memset(a,0, sizeof(a));
        memset(ans,0, sizeof(ans));
        int cnt=0;
        while(x>0)
        {
            a[++cnt]=x%10;
            x/=10;
        }
        for(int i=cnt-1;i>=1;--i)
        {
            for(int j=1;j<=9;++j)
            {
                for(int k=0;k<=9;++k)
                {
                    ans[k]+=dp[i][j][k];
                }
            }
        }
        for(int i=1;i<a[cnt];++i)
        {
            for(int k=0;k<=9;++k)
            {
                ans[k]+=dp[cnt][i][k];
            }
        }
        for(int i=cnt-1;i>=1;--i)
        {
            for(int j=0;j<a[i];++j)
            {
                for(int k=0;k<=9;++k)
                {
                    ans[k]+=dp[i][j][k];
                }
            }
            for(int j=cnt;j>i;--j)////2134  567
            {
                ans[a[j]]+=a[i]*cal(i-1);
            }
        }
    }
    ll sum[15];
    int main()
    {
        ll x,y;
        init();
        scanf("%lld%lld",&x,&y);
        count(y+1);
        for(int i=0;i<=9;++i)
        {
            sum[i]=ans[i];
        }
        count(x);
        for(int i=0;i<=9;++i)
        {
            sum[i]-=ans[i];
        }
        for(int i=0;i<=9;++i)
        {
            if(i!=0) printf(" ");
            printf("%lld",sum[i]);
        }
        printf("
    ");
        return 0;
    }
    

      

  • 相关阅读:
    jdk1.8安装教程
    实现负载均衡的两种方式
    Java中ArrayList和LinkedList区别
    做一个英译中的命令行工具
    在 IDEA 中运行 Spark 程序报错:Multiple sources found for text.......please specify the fully qualified class name
    Android消息处理:EventBus、BroadCast和Handler-优缺点比较(转)
    C#流总结(文件流、内存流、网络流、BufferedStream、StreamReader/StreamWriter、TextReader/TextWriter、转载)
    Android相关知识
    物联网相关知识
    Mqtt相关知识
  • 原文地址:https://www.cnblogs.com/zyf3855923/p/10347401.html
Copyright © 2011-2022 走看看