1. 程式人生 > >數位DP入門 —— hdu2089 不要62

數位DP入門 —— hdu2089 不要62

數位DP是一種用來計數的DP, 如果給你一道題, 讓你去統計區間[l, r]之間滿足某種條件的數字個數, 在沒接觸數位之前很容易想到的就是暴力判斷。但當資料範圍較大時這種方法就不可行了, 這時候我們就可能要用的差分的思想確定一個遞推關係, 來更方便更高效的求解, 這就是數位DP。這種理論的東西可能寫出來幫助不大, 數位DP的思想是通過自己對問題的思考逐漸建立的, 下面是我選的一個例題來幫助初步理解數位DP。

HDU 2089 —— 不要62

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

直接統計對於暴力列舉很好求,但是對於數位dp並不容易,所以我們還需要用到差分的思想,即統計0到b+1(注意不是b,至於為什麼後面會講)和0到a的滿足條件的個數,再兩者相減

進一步化簡,求0到i位數不含4和62的個數

i=1,求0~9的滿足條件的個數

i=2,求0~99的滿足條件的個數

i=3,求0~999的滿足條件的個數

i=4,求0~9999的滿足條件的個數

...

用dp[i][0]表示i位數中幸運數的個數

用dp[i][1]表示i位數中以2開頭的幸運數的個數

用dp[i][2]表示i位數中非幸運數的個數

那麼,就有以下的遞推公式

dp[i][0]=dp[i-1][0]*9-dp[i-1][1]//表示前i-1位數字中的幸運數前面加上除4以外的0~9的其他數字,共9個,還要減去前i-1位數字中的以2開頭的幸運數加上這一位以6開頭的數字的個數

dp[i][1]=dp[i-1][0]//表示前i-1位數字中的幸運數加上這一位的2

dp[i][2]=dp[i-1][2]*10+dp[i-1][1]+dp[i-1][0]//表示前面已經不合法的數字這一位無論放什麼都不合法,所以0~9隨便放,加上前i-1位數字中的以2開頭的幸運數加上這一位的6,再加上前i-1位數字中的幸運數加上這一位的4的個數

初始值 dp[0][0]=1,其他均為0

根據初始值和遞推公式,我們就能得到從0到任意i位數字的吉利數字的個數。

找到0 ~ n 的吉利數字的個數

我們先求出0 ~ n 之間非吉利數字的個數,用總數減去即可。那,非吉利數字的個數怎麼求呢?

用具體的數字舉例來說吧:設 n = 583626

用digit[10]記錄n+1每一位對應的數字,此例中有6位數字(令cnt = 6 表示數字位數),分別是

digit[6] = 5

digit[5] = 8

digit[4] = 3

digit[3] = 6

digit[2] = 2

digit[1] = 7

digit[0] = 任意數字,佔位用的

用sum記錄非吉利數字的個數,初始化為0

需要一個bool量 flag,記錄是否出現了非吉利數字。初始化為false, 未出現。

我們從數字的最高位起進行判斷:digit[6] = 5, 我們求 0 ~ 499999 之間非吉利數的個數。

  首先:加上0 ~ 99999中所有非吉利數字前面新增0~4的任意一個數字的情況 sum += dp[5][2] * digit[6]

  其次:5大於4,故我們要加上 0~99999中所有吉利數字前面新增4的情況 sum += dp[5][0]

接著,判斷第5位digit[5] = 8,即判斷500000 ~ 579999 之間的非吉利數字的個數,其實就是判斷0 ~ 79999之間的,前面的數字不是6就沒有什麼用

  首先:加上0 ~ 9999中所有非吉利數字前面新增0~7的任意一個數字的情況 sum += dp[4][2] * digit[5]

  其次:8大於4,故我們要加上 0~9999中所有吉利數字前面新增4的情況 sum += dp[4][0]

  此外:8大於6,故我們要加上0~9999中所有以2開頭的吉利數字前新增6的情況 sum += dp[4][1]

接著,判斷第4位digit[4] = 3,即判斷580000 ~ 582999 之間的非吉利數字的個數,其實就是判斷0 ~ 2999之間的

  首先:加上0 ~ 999中所有非吉利數字前面新增0~2的任意一個數字的情況 sum += dp[3][2] * digit[4]

  其次:2小於4,沒有需要特別考慮的

  此外:2小於6,沒有需要特別考慮的

接著,判斷第3位digit[3] = 6,即判斷583000 ~ 583599 之間的非吉利數字的個數,其實就是判斷0 ~ 599之間的

  首先:加上0 ~ 99中所有非吉利數字前面新增0~5的任意一個數字的情況 sum += dp[2][2] * digit[3]

  其次:6大於4,故我們要加上 0~99中所有吉利數字前面新增4的情況 sum += dp[2][0]

接著,判斷第2位digit[2] = 2,即判斷583600 ~ 583619 之間的非吉利數字的個數,其實就是判斷0 ~ 19之間的,

  首先:加上0 ~ 9中所有非吉利數字前面新增0~1的任意一個數字的情況 sum += dp[1][2] * digit[2]

  其次:2小於4,沒有需要特別考慮的

  此外:2小於6,沒有需要特別考慮的

  但是,需要注意的是,這裡判斷的數字出現了62,我們要把flag標識為true。

最後,判斷第1位digit[1] = 7, 判斷583620 ~ 583626但是這裡flag為true了,表示前面的數字裡面已經包含了非吉利數字,所以後面需要把所有的數字情況都加入到非吉利裡面。(正是因為每次判斷的數字末尾都比該位的數字少1,所以最開始要記錄n + 1 的值)

sum += digit[1] * dp[0][2] + digit[1] * dp[0][0]

1 #include<bits/stdc++.h>
 2 #define in(i) (i=read())
 3 using namespace std;
 4 int read() {
 5   int ans=0,f=1; char i=getchar();
 6   while(i<'0'||i>'9') {if(i=='-') f=-1; i=getchar();}
 7   while(i>='0'&&i<='9') {ans=(ans<<3)+(ans<<1)+i-'0'; i=getchar();}
 8   return ans*f;
 9 }
10 int dp[10][3],digit[15];
11 void init() {
12   memset(dp,0,sizeof(dp));
13   dp[0][0]=1;
14   for(int i=1;i<=8;i++) {
15     dp[i][0]=dp[i-1][0]*9-dp[i-1][1];
16     dp[i][1]=dp[i-1][0];
17     dp[i][2]=dp[i-1][2]*10+dp[i-1][1]+dp[i-1][0];
18   }
19 }
20 int solve(int x)
21 {
22   memset(digit,0,sizeof(digit));
23   int cnt=0,tmp=x;
24   while(tmp) {
25     digit[++cnt]=tmp%10;
26     tmp/=10;
27   }
28   digit[cnt+1]=0; int flag=0,ans=0;
29   for(int i=cnt;i>=1;i--) {
30     ans+=digit[i]*dp[i-1][2];
31     if(flag) ans+=digit[i]*dp[i-1][0];
32     else {
33       if(digit[i]>4) ans+=dp[i-1][0];
34       if(digit[i]>6) ans+=dp[i-1][1];
35       if(digit[i+1]==6 && digit[i]>2) ans+=dp[i][1];
36     }
37     if(digit[i]==4 || (digit[i+1]==6 && digit[i]==2)) flag=1;
38   }
39   return x-ans;
40 }
41 int main()
42 {
43   int a,b;
44   init();
45   while(1) {
46     in(a);in(b);
47     if(!a && !b)  break;
48     cout<<solve(b+1)-solve(a)<<endl;
49   }
50  return 0;
51 }

最後說那個b+1的情況,我們看到程式碼中有判斷digit[i]>4和digit[i]>6等類似的語句,我們處理第i位時,實際上是處理0~digit[i]-1,即[ 0,digit[i] ),而把digit[i]放到下一次去判斷,但我們處理個位時,最後一個是不會去統計的,所以我們把統計的範圍+1,即為[ 0,digit[i]+1 )-->[ 0,digit[i] ],所以就有了solve(b+1)-solve(a)這樣的語句

相關推薦

數位DP入門 —— hdu2089 不要62

數位DP是一種用來計數的DP, 如果給你一道題, 讓你去統計區間[l, r]之間滿足某種條件的數字個數, 在沒接觸數位之前很容易想到的就是暴力判斷。但當資料範圍較大時這種方法就不可行了, 這時候我們就可能要用的差分的思想確定一個遞推關係, 來更方便更高效的求解, 這就是數位D

數位dp入門 hdu2089 不要62

題意: 給定一個區間[n,m] (0< n ≤ m<1000000),找出不含4和’62’的數的個數 (ps:開始以為直接暴力可以。。貌似可以,但是直接TLE了2333).其實是數位

數位dphdu2089 不要62

clu scanf spa print algo std can dfs fin 好吧,雖然是道水題但是是第一次接觸數位dp所以還是要記錄一下。 其實就是因為如果按數的大小枚舉的話很不方便所以就按數位枚舉並進行記憶化。 #include<iostream> #

HDU2089——不要62數位dp入門

不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 35612    Accepted Submiss

HDU2089不要62數位DP入門題)

Description 給定mm,nn,求mm到nn的所有整數中及不含’4’也不含’62’的數的個數。 Solution 直接數位DP即可。。。 Source /*************

hdu2089——不要62(數位DP入門)

題意 杭州人稱那些傻乎乎粘嗒嗒的人為62(音:laoer)。  杭州交通管理局經常會擴充一些的士車牌照,新近出來一個好訊息,以後上牌照,不再含有不吉利的數字了,這樣一來,就可以消除個別的士司機和乘客的心理障礙,更安全地服務大眾。  不吉利的數字為所有含有4或62的號碼。

hdu 2089 不要62 數位DP入門

dfs += ret memset tro int 入門 space spa 題目鏈接: http://acm.hdu.edu.cn/showproblem.php?pid=2089 題意: 統計區間 [a,b] 中不含 4 和 62 的數字有多少個。 思路: 學習了數位d

數位dp入門】【HDU208962

ret main ont scanf size hdu2089 con %d tmp 為了我的點歪的技能樹…… 所以開始補一些sb的東西…… #include<bits/stdc++.h> typedef long long ll; using namespa

hdu2089不要62(數位dp)

消息 推斷 位置 text can bsp 號碼 con tput 不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submissi

HDU2089 ------不要62數位dp

判斷 tom name pop iostream blank 位置 play show 不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others

hdu 2089 不要62 數位dp入門

stream 然而 != 1的個數 tdi 記憶化 代碼 pan += 題意:求一個範圍內的數字,約束條件為不含有數字4以及62 typedef long long ll; int a[20]; ll dp[20][state];//不同題目狀態不同 ll d

HDU-2089不要62-暴力或數位DP入門

之前 scanf href esp ostream += log main string 不要62 題意:給定區間,求在這個區間中有多少個數字,不包含4且不包含62; 這道題作為數位DP的入門題; 暴力也是可以過 #include<cstdio> #inclu

HDU 2089 不要62數位DP入門題】

pan eps ava con 所有 數據 strong mon sub 不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submi

HDU2089 不要62數位DP

傳送門 【題目分析】 和windy數一題類似,不過限制條件改為了連續的62和4,列舉當前位填的數字的時候判斷一下即可。 【程式碼~】 #include<bits/stdc++.h> using namespace std; typedef long long LL; co

數位DP入門 HDU 2089 不要62 註釋詳解

題意:輸出一個區間【m,n】內所有不含“62"and"4" 的數字個數;n<=10^7;  數位dp一般應用於:   求出在給定區間[A,B]內,符合條件P(i)的數i的個數.   條件P(i)一般與數的大小無關,而與 數的組成 有關. 具體的理論過程我就不解釋

HDU2089 不要62 數位DP

杭州人稱那些傻乎乎粘嗒嗒的人為62(音:laoer)。杭州交通管理局經常會擴充一些的士車牌照,新近出來一個好訊息,以後上牌照,不再含有不吉利的數字了,這樣一來,就可以消除個別的士司機和乘客的心理障礙,更安全地服務大眾。不吉利的數字為所有含有4或62的號碼。例如:62315 73418 88914都屬於不吉利號

hdu2089 不要62 (數位dp)

Problem Description 杭州人稱那些傻乎乎粘嗒嗒的人為62(音:laoer)。 杭州交通管理局經常會擴充一些的士車牌照,新近出來一個好訊息,以後上牌照,不再含有不吉利的數字了,這樣一來

HDU2089 不要62數位DP

不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 28103    Accepted Submissi

HDU2089 不要62數位DP 基礎】

不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 60064    Accepted Submissio