download.csdn.net/detail/u012959992/8892265
一般是求小於等於數字n的某些特徵數字個數,或者是區間[l,r]的之間的某些特徵數字個數,後者一般可以轉換成求差的方式來做。
數字處理函式
int f(int num)
return dfs( pos, s , true );
}
digit為處理串的每個數字的數值。
s為初始的狀態。
如果有其他限定條件,dfs
中可以增加引數。
dfs
函式
int dfs(int l, int s, bool jud)
f為記憶化陣列;
l為當前處理串的第l位(權重表示法,也即後面剩下l+1位待填數);
s為之前數字的狀態(如果要求後面的數滿足什麼狀態,也可以再記乙個目標狀態t之類,for的時候列舉下t);
jud表示之前的數是否是上界的字首(即後面的數能否任意填)。
for迴圈列舉數字時,要注意是否能列舉0,以及0對於狀態的影響,有的題目前導0和中間的0是等價的,但有的不是,對於後者可以在dfs時再加乙個狀態變數z,表示前面是否全部是前導0,也可以看是否是首位,然後外面統計時候列舉一下位數。it depends.
於是關鍵就在怎麼設計狀態。當然做多了之後狀態一眼就可以瞄出來。
前導零的判斷
int dfs(bool zero)
......
ans+=dfs(zero&&i==0
)//不區分前導零與零
ans+=dfs(zero&&i==0&&l>1
)//區分前導零與零
a. 【cf55d】
題意給定區間[l,r],求區間完美數字的個數。(乙個數字是完美數字當且僅當該數字可整除其所有數字上的非零數)
思路位數上的數字只需要考慮2~9,因此用乙個數字1<<8
來表示有哪些數字出現過。
如果乙個數是所有位數的倍數,那麼一定是其最小公倍數的倍數,其所有的最小公倍數是2520,所以求和的時候直接對2520取餘。
dp[l][cnt][sum]
,l為數字長度,cnt為位數上有哪些數,sum為數字和。
dfs(int l,int cnt,int sum,bool jud)
,jud為是否為邊界。
b. 【hdu4352】
題意尋找[l,r]中間所有數字串中lis(最長上公升子串行)為k的數字和。
思路lis運用動態規劃可以在o(nlogn)的時間複雜度解決,此略。
因為最多只有0~9十個數字,因此可以預處理。
sta為lis的狀態,siz[sta]中儲存lis的長度(即二進位制中1的個數),nex[sta][l]為在sta中插入數字l之後的lis狀態。
dp[l][sta][k]
:l為數字長度,sta為當前lis的狀態,k為所要求的lis長度。
dfs(int l,int sta,bool zero,bool jud)
:zero判斷是否為前導零,jud為是否為邊界。
注意:dp[l][sta][k]中,k並不是必須的,然而因為本題樣例組數過多且k很小,所以選擇增加一維表狀態而不是每次都初始化以節約時間。
c. 【hdu2089】
題意[l,r]中,不含4或62的數字個數。
思路dp[l][six]
:l為數字長度,six為最後一位數字是否為6。
dfs(int l,bool six,bool jud)
,jud判斷是否為邊界。
d. 【hdu3555】
題意數字中含有49的數字個數。
思路偷了下懶,用c題的**求不含49的個數,然後做差,居然過了= =b。其實這道題打表都行= =b。
e. 【hdu3252】
題意數字[l,r]中,round number數字的個數。round number即數字轉換成二進位制後0的個數大於等於1的個數。
思路digit陣列中直接儲存該數字的二進位制形式。
dp[l][cnt1][cnt2][zero]
:l為數字長度,cnt1為數字0的數字個數,cnt2為數字1的數字個數,zero判斷是否為前導零。
dfs(int l,int cnt1,int cnt2,bool zero,bool jud)
:jud判斷是否為邊界。
f. 【hdu3709】
題意統計區間[l,r]中,balanced number的數字個數。balanced number即對於任意乙個數字串,假設其有乙個數字位,它左邊的數字乘距離的和等於它右邊的數字乘距離的和,則其為balanced number。
思路列舉作為平衡點的數字位數,最後要減掉(pos-1)因為對於0,計算了0,00,000…重複計算了pos次,只需要保留一次。
dp[l][o][pos]
:長度為l,平衡點位置為o時的當前狀態(pos為0時表示平衡,pos>0則為左邊的沉,pos<0則為右邊的沉)
dfs(int l,int o,int pos,bool jud)
:jud判斷是否為邊界。
注意只要pos<0就可以返回false。
g. 【hdu3652】
題意[1,n]的含數字13且為13的倍數的數字個數。
思路dp[l][mod][iso][has]
:l為數字長度,mod為當前數字對13的取餘值,iso為是否存在,has為最後一位是否為數字1。
int dfs(int l,int mod,bool iso,bool has,bool jud)
:jud判斷是否為邊界值。
h. 【hdu4734】
題意對於每個數字x,都有乙個f(x)值,讓你求[0,b]中,函式值小於等於f(a)的數字個數。
思路首先計算出f(a)。
dp[l][sum]
:l為當前數字長度,sum為f(a)減去之前列舉的數字的數字差(如果差為正則代表f(a)大)。
dfs(int l,int sum,bool jud)
:jud判斷是否為邊界。
i. 【zoj3494】
題意區間[l,r]的數字轉換成bcd
之後,不含有forbidden code(即長度不超過20的01組成的數字串)的數字個數。
思路forbidden code可以用trie樹
儲存。
dp[l][pos]
:l為數字長度,pos為樹上的位置。
dfs(int l,int pos,bool zero,bool jud)
:zero判斷是否為前導零(注意和數字0區分),jud判斷是否為邊界。
j. 【hdu4507】
題意區間[l,r]中,和7無關的數字的平方和是多少?含7的數、數字和為7的倍數、數為7的倍數都是和7有關的數字。
思路dp[len][sum][remain]
:len為長度,sum為數字和,remain為字首和。
dfs(int len,int sum,int remain,bool jud)
jud判斷是否為邊界。
維護三個數字:個數,和,平方和。
a[1]^2 + a[2]^2 + … + a[n]^2,新式是 (a[1]+b)^2 + (a[2]+b)^2 + … + (a[n]+b)^2
,按照這樣的原理求字首的平方和。
k. 【spoj10606】
題意求[l,r]中positive integer的數字個數。positive integer就是對於乙個數字串,偶數數字各有奇數個,奇數數字各有偶數個。
思路用乙個三進製數字表示每個數字的狀態,0為未出現過,1為出現過奇數次,2為出現過偶數次。
dp[l][s]
:l為數字長度,s為當前所有數字出現的狀態。
dfs(int l,int s,bool zero,bool jud)
:zero判斷是否為前導零,jud判斷是否為邊界。
劉聰《**數字類統計問題》
高逸涵《數字計數問題解法研究》
以及各種搜題解是搜到的blog= =b
數字dp小結
數字dp可不是對於數的每一位進行dp,而是指對於這個數的組成進行dp。對於數的每一位進行dp,只是數字dp的一類題目。題目描述 現在有兩個要求 這是乙個 n n 106 n n leq 10 6 n n 10 6 位的數 不含前導零 相鄰的兩位的差值大於等於 p p 9 p p leq 9 p p ...
數字dp小結
在這裡寫個總結吧 對於數字dp,之前學的時候,學得很唬,現在又學了一發,還是感覺這個東西不是很好掌控.這個東西有他的思想,當然也有一定的套路和模板,我大概可以掌握模板,了解了一些套路,對他的思想也有了一些認識.雖然說思想是根本,但是掌握套路和模板也是必不可少的,不過只知道套路和模板對於一些比較活的題...
數字DP小結
數字dp是用來解決一段區間內,存在多少個數滿足某個數的性質的問題 題目中輸入的數可能會爆int,一般為0 a b 2 10 9 數字dp中處理的是每乙個數的位,而不是這個數的本身 也就是說1024,2024在後面三位在性質上是一樣的,所以dp會儲存這三位的狀態 這樣就節省了時間複雜度 一般來說,數字...