hdu2089 not 62 (digital dp)

Reprinted from https://blog.csdn.net/zhangxian___/article/details/75304335

don't 62

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 52432    Accepted Submission(s): 19956


Problem Description
Hangzhou people call those goofy and sticky people 62 (sound: laoer).
The Hangzhou Traffic Administration often expands some taxi license plates. There is good news recently. In the future, the license plate will no longer contain unlucky numbers. In this way, the psychological barriers of individual taxi drivers and passengers can be eliminated, and more Safely serve the public.
Unlucky numbers are all numbers containing 4 or 62. For example:
62315 73418 88914
are all unlucky numbers. However, although 61152 contains 6 and 2, it is not a 62 consecutive number, so it does not belong to the list of unlucky numbers.
Your task is to deduce how many new taxis are actually licensed by the traffic authority this time for each license plate interval number given.
 

Input
The input is all integer pairs n and m (0<n≤m<1000000). If an integer pair of 0 is encountered, the input ends.
 

Output
For each pair of integers, output a statistic that does not contain unlucky numbers on a single line.
 

Sample Input
 
  
1 100 0 0
 

Sample Output
 
  
80
 

Author
qianneng
 

Source
 

Digital DP is actually very flexible, so you must not expect that one article will cover all the questions of digital DP. This article can only explain one situation clearly, and then summarize other situations, and slowly realize it through continuous summarization. This idea may be able to reach a level where you can flexibly apply it as soon as you see the topic. (In fact, DP is like this...)

 

The digital DP to be mentioned in this article is the simplest digital DP: http://acm.hdu.edu.cn/showproblem.php?pid=2089

      The main idea of ​​the title: For multiple sets of data, each time a given interval [n, m], find the number of numbers that do not have "62" or "4" in n to m.

      For example, 62315 contains 62, and 88914 contains 4, both of which are illegal. 0<n<=m<1000000

Just think: If we can have a function count(int x), it can return the number of numbers between [0,x] that match the meaning of the question. So is the direct output of count(m)-count(n-1) the answer?

Ok, so now our focus is on how to make this function. We need an array. (dp is originally space for time)

 

We set up an array f[i][j], which represents the number of i digits, the highest digit is the number of j, and how many numbers meet the meaning of the question . For example f[1][2]=1; f[1][4]=0; f[2][6]=8 (60,61,63,64,65,66,67,68,69).

 

Let's not pay attention to the use of this f first, let's focus on how to find f itself. First f[1][i]=0(if i==4), f[1][i]=1(if i!=4) (0<=i<=9). This step is obvious, then according to the data range of this question, it is enough to recurse to f[7][i]. Then with a little understanding, you can come up with a recursive formula:

  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>
using namespace std;
int dp[8][10];
long long count(int x){
    int t=0;
    int v[10];
    while(x){
        t++;
        v[t]=x%10;
        x=x/10;
    }
    v[t+1]=0;
    long long ans=0;
    for(int i=t;i>=1;i--){
        for(int j=0;j<v[i];j++){
            if(j!=4&&!(v[i+1]==6&&j==2))
            ans+=dp[i][j];
        }
        if(v[i]==4)
            break;
        if(v[i+1]==6&&v[i]==2)
            break;
    }
    return ans;
}
int main(){
    dp[0][0]=1;
    for(int i=1;i<=7;i++){
        for(int j=0;j<=9;j++){
            if(j==4)
                dp[i][j]=0;
            else{
                for(int k=0;k<=9;k++)
                    dp[i][j]+=dp[i-1][k];
                if(j==6)
                    dp[i][j]-=dp[i-1][2];
            }
        }
    }
    int x,y;
    while(~scanf("%d %d",&x,&y)){
        if(x==0&&y==0)
            break;
        long long tx=count(x);
        long long ty=count(y+1);
        printf("%lld\n",ty-tx);
    }
    return 0;
}


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325524603&siteId=291194637