數字DP小結

2021-07-03 18:31:23 字數 4103 閱讀 2754

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會儲存這三位的狀態 這樣就節省了時間複雜度 一般來說,數字...