HDU2089 do not need 62 (digital dp, 3 ways of writing)

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 an integer pair n, m (0

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

ideas

Learning digital dp:
1. Preliminary exploration of digital dp

This question is a digital dp, which gives an interval to find the number of numbers that do not contain a sum in [l,r]this interval .462

Regarding the digital dp, we generally calculate it like this. First of all, we know that a number is aless than another number b, then aa certain digit must be less than ba certain digit. The digital dp uses this idea to solve the problem:

For example, [1,456]to the number of numbers that satisfy a certain condition, first split the number into each position:

Location 4 5 6
Ranges 4 5 0~6
Ranges 4 0~4 0~9
Ranges 0~3 0~9 0~9

There are three ways to do this online.

method 1:

As in the method explained above ppt,
we define:

dp[i][j]: Indicates the inumber of digits, how many digits in the first place jmeet the requirements.

For a number, for example 335, corresponds dp[3][3], but the numbers that satisfy the requirement are 336, 358...these numbers exceed 335...

So through dp[3][0], dp[3][1], dp[3][2]find the three-digit number that satisfies the condition with the first digit less than 3, dp[3][0]the number obtained is 001,052,093...that is, all the one- or two-digit numbers that satisfy the condition...we get all 2xx, 1xx, 0xx.

The next requirement is a number such as 334, 327 whose first position is 3 and satisfies the condition. Since the first position can only be 3, that is, the first position has been selected, then we only need to choose a number less than 35. In the same way as above, we only need to find dp[2][0], dp[2][1], dp[2][2], dp[2][3], dp[2][4]to find the two-digit number whose first position is less than 3. At this time, the obtained is 32x, 31x, 30x.

Then we ask for all numbers less than 5 and it's ok. Less than 5 is dp[0], dp[1], dp[2], dp[3], dp[4]. At this time, the obtained value is 53x.

So far, all the numbers less than 335 and satisfying are all obtained.

Code:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <sstream>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
#include<list>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
const int N=40+10;
const int M=N*N;
int dp[10][10];
void init()
{
    mem(dp,0);
    dp[0][0]=1;
    for(int i=1; i<=7; i++) //枚举位数
        for(int j=0; j<=9; j++) //枚举第i位
            for(int k=0; k<=9; k++) //枚举第i-1位
                if(j!=4&&!(j==6&&k==2))
                    dp[i][j]+=dp[i-1][k];
}
int pos[10];
int solve(int n)
{
    int ans=0,len=0;
    while(n)
    {
        pos[++len]=n%10;
        n/=10;
    }
    pos[len+1]=0;
    for(int i=len; i>=1; i--)
    {
        for(int j=0; j<pos[i]; j++)
            if(j!=4&&!(pos[i+1]==6&&j==2))
                ans+=dp[i][j];
        if(pos[i]==4||(pos[i+1]==6&&pos[i]==2))
            break;
    }
    return ans;
}
int main()
{
    int n,m;
    init();
    while(scanf("%d%d",&n,&m)&&(n||m))
        printf("%d\n",solve(m+1)-solve(n));
    return 0;
}

Method 2:

Here kuangbin's how:

definition:

dp[i][0]:长度为i,吉利数字的个数
dp[i][1]:长度为i,最高位为2的吉利数字个数
dp[i][2]:长度为i,不吉利数字的个数

First, for the 6 digits of this question, preprocess these dp[i][0~3]values, and then calculate the number of unlucky numbers in this interval and then subtract the number of lucky numbers, which is the answer. See the notes for details. :

Code:

#include <bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
int dp[10][3];
/*
dp[i][0]:长度为i,吉利数字的个数
dp[i][1]:长度为i,最高位为2的吉利数字个数
dp[i][2]:长度为i,不吉利数字的个数
*/
void init()
{
    dp[0][0]=1;
    dp[0][1]=0;
    dp[0][2]=0;
    for(int i=1;i<=6;i++)//最高6位数
    {
        dp[i][0]=dp[i-1][0]*9-dp[i-1][1];//在最高位加上除4之外的9个数字,和2之前的6
        dp[i][1]=dp[i-1][0];//在i-1的最高位加上2
        dp[i][2]=dp[i-1][2]*10+dp[i-1][0]+dp[i-1][1];
        //在已有不吉利数字前加任意数字,或者无不吉利数字的最高位加4,或者在2前面加6
    }
}
int bit[10];
int solve(int n)
{
    int len=0,tmp=n;
    while(n)
    {
        bit[++len]=n%10;
        n/=10;
    }
    bit[len+1]=0;
    int ans=0,flag=0;//ans记录不吉利数字个数
    //flag不吉利数字是否出现过
    for(int i=len;i>=1;i--)
    {
        ans+=dp[i-1][2]*bit[i];//每一个不吉利数字加上之后还是不吉利
        if(flag)//高位出现4或62
            ans+=dp[i-1][0]*bit[i];//前一位的吉利数字的个数加上0~bit[i]-1个数字
        else
        {
            if(bit[i]>4) ans+=dp[i-1][0];//当前选择范围是0~bit[i],必然包含4,所以加上4
            if(bit[i]>6) ans+=dp[i-1][1];//当前选择范围是0~bit[i],必然包含6,所以加上6
            if(bit[i+1]==6&&bit[i]>2) ans+=dp[i][1];//当前位置大于2且前一位是6,所以加上以2开头的
            if(bit[i]==4||(bit[i+1]==6&&bit[i]==2)) flag=1;
        }
    }
    if(flag) ans++;
    return tmp-ans;
}
int main()
{
    //freopen("in.txt","r",stdin);
    int n,m;
    init();
    while(scanf("%d%d",&n,&m)&&(n||m))
    {
        printf("%d\n",solve(m)-solve(n-1));
    }
    return 0;
}

Method 3:

This method uses memoized search, and the code is written very concisely.
Reference blog:

definition:

  • dp[pos][0]Indicates the number of states that are currently enumerated to the first posdigit that is not 6
  • dp[pos][1]Indicates the number of states currently enumerated to the first posdigit 6

Because only one digit is involved when judging whether it is 4, it is a good judgment, but when judging the state of 62, it is necessary to consider whether the previous digit is 6.

code

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <sstream>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
#include<list>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define inf 0x3f3f3f3f
typedef long long ll;
int dp[10][2],bit[10];
//dfs的参数分别代表当前位数,上一个位置是否是6,当前的位置有没有枚举限制
int dfs(int pos,int six,int flag)
{
    if(pos==0) return 1;
    if(!flag&&dp[pos][six]!=-1)
        return dp[pos][six];
    int len=flag?bit[pos]:9;
    int res=0;
    for(int i=0; i<=len; i++)
    {
        if(i==4||six&&i==2) continue;
        res+=dfs(pos-1,i==6,flag&&i==len);
    }
    if(!flag) dp[pos][six]=res;
    return res;
}
int solve(int n)
{
    int ans=0,len=0;
    while(n)
    {
        bit[++len]=n%10;
        n/=10;
    }
    mem(dp,-1);
    return dfs(len,false,true);
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)&&(n||m))
    {
        printf("%d\n",solve(m)-solve(n-1));
    }
    return 0;
}

Guess you like

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