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

    数位DP其实是很灵活的,所以一定不要奢求一篇文章就会遍所有数位DP的题,这一篇只能是讲清楚一种情况,其他情况遇到再总结,在不断总结中慢慢体会这个思想,以后说不定就能达到一看到题目就能灵活运用的水平。(其实DP都是这样……)

    这一篇要说的数位DP是一道最简单的数位DP:http://acm.hdu.edu.cn/showproblem.php?pid=2089

          题目大意:多组数据,每次给定区间[n,m],求在n到m中没有“62“或“4“的数的个数。

          如62315包含62,88914包含4,这两个数都是不合法的。0<n<=m<1000000

    试想:我们如果能有一个函数count(int x),可以返回[0,x]之间符合题意的数的个数。那么是不是直接输出count(m)-count(n-1)就是答案?

    好,那么下面我们的关注点就在于怎么做出这个函数。我们需要一个数组。(dp原本就是空间换时间)

    我们设一个数组f[i][j],表示i位数,最高位是j的数,符合题意的数有多少个。比如f[1][2]=1; f[1][4]=0; f[2][6]=8 (60,61,63,64,65,66,67,68,69).

    我们先不关注这个f有什么用,我们先关注f本身怎么求。首先f[1][i]=0(if i==4),f[1][i]=1(if i!=4) (0<=i<=9)。这一步是很显然的,那么根据这个题的数据范围,只需要递推到f[7][i]就够用了。那么稍微理解一下,可以想出递推式:

      f[i][j]=

        if (j==4) f[i][j]=0

        else if (j!=6) f[i][j]=Σf[i-1][k] (k=0,1,2,3,4,5,6,7,8,9)

        else if (j==6) f[i][j]=Σf[i-1][k] (k=0,1,3,4,5,6,7,8,9)

    上面的式子也是很显然的,如果觉得不显然可以这样想:i位数,最高位是j的符合条件的数,如果j是4,肯定都不符合条件(因为题目不让有4),所以直接是0;如果j不是6,那么它后面随便取,只要符合题意就可以,所以是f[i-1][k],k可以随便取的和;如果j是6,后面只要不是2就行,所以是f[i-1][k],k除了2都可以,求和。

    这里要说明一下,认为00052是长度为5,首位为0的符合条件的数,052是长度为3首位为0符合条件的数。

    那么现在我们已经得到了f数组,再重申一下它的含义:i位数,最高位是j的数,符合题意的数有多少个。

    现在我们就要关注怎么利用f数组做出上面我们说的那个函数count(int x),它可以求出[0,x]中符合题意的数有多少个。

    那么我们做这样一个函数int solve(int x) 它可以返回[0,x)中符合题意的有多少个。那么solve(x+1)实际上与count(x)是等价的。

    那么现在问题转化成了:小于x,符合题意的数有多少个?

    很简单,既然小于,从最高位开始比,必定有一位要严格小于x(前面的都相等)。所以我们就枚举哪一位严格小于(前面的都相等)。

    假设我们现在把x分成了a1,a2,...,aL这样一个数组,长度为L,aL是最高位。

    那么结果实际上就是这样:长度为L,最高位取[0,aL-1]的所有的符合题意数的和;再加上长度为L-1,最高位取aL次高位取[0,aL-1-1]的所有符合题意数的和;再加上……;一直到第一位。

    上面有一句话之所以标粗体,是因为这句话并不是对的,但是为了好看,就先这样写着。因为我们还需要考虑这种情况:最高位aL如果是4,那么这句话直接就可以终止了,因为粗体这句话前面的那句话“最高位取aL”是不能成立的。还要考虑这种情况:最高位aL如果是6,那么这里并不是能取[0,aL-1-1]的所有(不能取2)。加上这些条件之后就很严谨了。

    把上面的汉字对应到题目里,就是我们前面求出来的f[L][0..aL-1]  f[L-1][0..aL-1-1],所以稍加思索之后就能写出程序了。

    #include<cstdio>
    
    const int maxn=10;
    long long f[maxn][10];
    
    void getdp()
    {
        f[0][0]=1;
        for (int i=1;i<10;i++)
        {
            for (int j=0;j<10;j++)
            {
                if (j==4) f[i][j]=0;
                else if (j==6)
                {
                    for (int k=0;k<10;k++)
                        f[i][j]+=f[i-1][k];
                    f[i][j]-=f[i-1][2];
                }
                else
                {
                    for (int k=0;k<10;k++)
                        f[i][j]+=f[i-1][k];
                }
            }
        }
    }
    
    int a[maxn];
    long long solve(int n)
    {
        a[0]=0;
        while (n)
        {
            a[++a[0]]=n%10;
            n/=10;
        }
        a[a[0]+1]=0;
        long long ans=0;
        for (int i=a[0];i>=1;i--)
        {
            for (int j=0;j<a[i];j++)
                if (j!=4 && !(a[i+1]==6 && j==2))
                    ans+=f[i][j];
            if (a[i]==4) break;
            if (a[i+1]==6 && a[i]==2) break;
        }
        return ans;
    }
    
    int main()
    {
        int n,m;
        getdp();
        while (scanf("%d %d",&n,&m)==2 && (n||m))
        {
            long long k1=solve(m+1);
            long long k2=solve(n);
            //printf("::%d,%d::",k1,k2);
            printf("%I64d
    ",k1-k2);
        }
        return 0;
    }
  • 相关阅读:
    Struts2+Spring3+Mybatis3开发环境搭建
    spring+struts2+mybatis
    【LeetCode】Populating Next Right Pointers in Each Node
    【LeetCode】Remove Duplicates from Sorted Array
    【LeetCode】Remove Duplicates from Sorted Array II
    【LeetCode】Binary Tree Inorder Traversal
    【LeetCode】Merge Two Sorted Lists
    【LeetCode】Reverse Integer
    【LeetCode】Same Tree
    【LeetCode】Maximum Depth of Binary Tree
  • 原文地址:https://www.cnblogs.com/acmsong/p/8157321.html
Copyright © 2011-2022 走看看