題目:
杭州人稱那些傻乎乎粘嗒嗒的人為62(音:laoer)。
杭州交通管理局經常會擴充一些的士車牌照,新近出來乙個好訊息,以後上牌照,不再含有不吉利的數字了,這樣一來,就可以消除個別的士司機和乘客的心理障礙,更安全地服務大眾。
不吉利的數字為所有含有4或62的號碼。例如:
62315 73418 88914
都屬於不吉利號碼。但是,61152雖然含有6和2,但不是62連號,所以不屬於不吉利數字之列。
你的任務是,對於每次給出的乙個牌照區間號,推斷出交管局今次又要實際上給多少輛新的士車上牌照了。
思路:寫了乙個蠻力演算法,直接超時了。之後各種想不出來,上網搜答案。結果發現有專門的解法,叫數字dp。之後看答案看了2個小時,那50行**翻來覆去看了好久,終於看明白了。
唉,大神們寫**的時候注釋都太精簡了,像我這種沒學過數字dp的看得很痛苦啊。
下面解析一下:
題目會給出兩個數字 m 和 n,我們要找到 【m, n】區間內,不含4與62的數字的個數。
①我們把問題拆解為兩個部分, 分別求0 ~ m - 1 和 0 ~ n 之間的不含4與62的數字的個數,然後相減。
②但是0~n中的不含4與62的值求解也很複雜,所以我們先進一步化簡,求0到 i 位數的不含4和62的數字個數。
比如:
i = 1,即求 0 ~ 9 中不含4和62的數字個數
i = 2,即求 0 ~ 99 中不含4和62的數字的個數
i = 3,即求 0 ~ 999 中不含4和62的數字個數
i = 4,即求 0 ~ 9999 中不含4和62的數字的個數
..... 以此類推
用dp[i][0] 來儲存 0 到 i 位數字中不含4和62的數字個數,即幸運數
用dp[i][1] 來儲存 0 到 i 位數字中以 2 開頭的幸運數。
用dp[i][2] 來儲存 0 到 i 位數字中的非幸運數,即包含4或者62的數字。
那麼,可以用下面的遞推公式
dp[i][0] = dp[i - 1][0] * 9 - dp[i - 1][1] // i 位數字中的幸運數個數 = (i - 1)位幸運數字前面加上0 - 9 中除去4以外的9個數字 - 以2開頭的(i - 1)位幸運數字前面加上了6.
dp[i][1] = dp[i - 1][0] // 0到 i 位數字中以2開頭的幸運數 = 0到 i 位數字中所有的幸運數字前面加上2
dp[i][2] = dp[i - 1][2] * 10 + dp[i - 1][0] + dp[i - 1][1] //0到 i 位的非吉利數 = 0到 i - 1 位的非吉利數前面加上0-9的任何數字 + i-1位的吉利數字前面加上了4 + i-1位以2開頭的吉利數字前面加上了6.
初始值: dp[0][0] = 1 dp[0][1] = dp[0][2] = 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]
總結一下,這部分的演算法如下:
int flag=0,ans=0;
for(int i=cnt;i>0;i--)
if(digit[i]==4 || (digit[i+1]==6 && digit[i]==2
)) flag=1
; }
整體的**如下:
#include#include#include
using
namespace
std;
int dp[10][3
];void init()
}int solve(int
x) digit[cnt+1]=0
;
int flag=0,ans=0
;
for(int i=cnt;i>0;i--)
if(digit[i]==4 || (digit[i+1]==6 && digit[i]==2
)) flag=1
; }
return x-ans; //
所有的數減去非吉利的數
}int
main()
return0;
}
網上有更簡潔的**,用dfs和狀態轉移做的,我沒看懂。
#include #include#include
using
namespace
std;
int dp[8][2],digit[8
];int dfs(int len,bool state,bool
fp)
if(!fp)
dp[len][state] =ret;
return
ret;
}int f(int
n)
return dfs(len,false,true);}
intmain()
return0;
}
第三種可以通過的方法是暴力打表,這個比較簡單
#include #includeint flag[1000001
];int
main()
temp/=10
; }
}while(1
)
printf(
"%d\n
",m-n+1-amount);
}return0;
}
DP 數字 DP 不要62
水一篇題解。掌握了數字 dp 的套路之後,10分鐘就可敲出這道題目。題目 不要62 做法 動態規劃 狀態表示 f i j f i j f i j 表示 i ii 位數,最高位是 j jj 的合法數字個數。預處理 只要不含4並且相鄰兩位不是6 2即可累加 狀態轉移步驟 1 不選最大可填數時 累加 2 ...
不要62(數字DP)
description 杭州人稱那些傻乎乎粘嗒嗒的人為62 音 laoer 杭州交通管理局經常會擴充一些的士車牌照,新近出來乙個好訊息,以後上牌照,不再含有不吉利的數字了,這樣一來,就可以消除個別的士司機和乘客的心理障礙,更安全地服務大眾。不吉利的數字為所有含有4或62的號碼。例如 62315 73...
不要62 (數字dp)
杭州人稱那些傻乎乎粘嗒嗒的人為62 音 laoer 杭州交通管理局經常會擴充一些的士車牌照,新近出來乙個好訊息,以後上牌照,不再含有不吉利的數字了,這樣一來,就可以消除個別的士司機和乘客的心理障礙,更安全地服務大眾。不吉利的數字為所有含有4或62的號碼。例如 62315 73418 88914 都屬...