數字dp,字面意思理解就是在數字的每一位上面去dp,動態規劃一般有兩種:遞推,記憶化搜尋(dfs)。這裡就是用的記憶化。
一般這種用在計數上面,對那些數字上面有限制的計數。
這裡上一道模板題:
題中就是要你統計1—n裡有「49」的個數。
dp[pos][sta]表示到第pos位,狀態為sta的總數。
我們一般是從高位往低位計數。我們這裡的狀態本來可以是10種(0,1,2,。。。9)但是我們可以把他們分為兩種(為4的,不為4的)
如果我們當前位為4,那麼下一位就不能為9.
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
ll dp[20]
[2];
int digit[20]
;//分解x,將各個位賦值給digit陣列
ll solve
(ll x)
//這裡limit為true,是因為我們第一位的前面想象為0,那麼就是有上界了(0為上界)。
return
dfs(pos,false,true);}
//pos表示當前第幾位
//sta表示前一位是否是4
//limit表示是否到達上界
ll dfs
(int pos,bool sta,bool limit)if(
!limit) dp[pos]
[sta]
=cnt;
return cnt;
}int
main()
}}
為什麼返回值那裡要if(!limit),我們假設上界是230,求1–230的不含「33」的個數。那麼我們百位訪問0的時候,因為沒有達到上界(2),所以十位我們可以訪問0-9,當十位訪問3的時候,個位我們可以訪問0,1,2,4(不能有「33」),所以有4個。但是當我們百位訪問2的時候,到達上界了,就是說十位,個位我們不能取0-9了,十位只能取到3,個位只能取到0。當我們十位取到3的時候,這是如果我們沒有(!limit),我們前面算到dp[0][1](當前位 為 個位,前面一位是3),我們會直接返回4,但是這裡我們只能訪問0,因為不能超過230。所以得limit。
後面的記憶化也要limit也是這樣。
hdu 4507
題目要求的是與7無關的數的平方和。這個數字dp不是計數的,而是求平方和的。
dp[pos][mod1][mod2]為當前位為pos,各個位數的和模7,數的和模7
首先我們先設定狀態,題目的要求:
1,沒有7,這個直接在迴圈裡面判斷進行了.
2.各位數之和不是7的倍數
3.這個數不是7的倍數。
2.3這兩個得在遞迴裡面去加,然後分情況。
可以對7取模,就有餘數為0,就是7的倍數,不為0,就是可以找的數。
這是條件,接下來看要求的平方和。
我們算乙個數的平方和,比如123,145,134這些都是合法的數,那求平方和就是,(100+23)^2+(100+45) ^2+(100+34) ^2=3100 ^2+2100*(23+45+34)+(23 ^2+45 ^2+34 ^2);
這樣我們假設我們求到pos為百位1的時候,我們求的平方和就為
從十位開始後面的數(加上百位後的各種條件合法後)的個數乘以
ipower[i]+ 2 * i power[i] * 十位後的數的總和 +十位後數的平方和。
temp為低位,ans為高位。可以看做temp為十位後面的,ans為百位
其實就是:
ans.cnt+=temp.cnt (合法的數)
ans.sum1+=temp.cnt * i * power[pos]+temp.sum1 (各個數的和)
ans.sum2+=i * power[pos] * i * power[pos] * temp.cnt+temp.sum2+2 * i * power[pos] * temp.sum1(各個數平方和)
為什麼要乘以個數,就是為百位1後面 有多個合法的數。
其他的都和簡單的數字dp差不多了。
要注意的點
solve(y)可能比solve(x-1)小,所以要+mod後取模,比如(6-3)%5=3
,但是6%5-3%5=-2, (-2+5)%5=3;
#include
#include
#include
#include
#include
#define ll long long
#define mod 1000000007
using namespace std;
struct nodedp[20]
[10][
10];int digit[20]
;ll power[25]
;node dfs
(int pos,
int mod1,
int mod2,bool limit);if
(mod1&&mod2) temp.cnt=1;
return temp;}if
(!limit&&dp[pos]
[mod1]
[mod2]
.cnt!=-1
)return dp[pos]
[mod1]
[mod2]
;int up=limit?digit[pos]:9
; node ans=
;for
(int i=
0;i<=up;i++)if
(!limit) dp[pos]
[mod1]
[mod2]
=ans;
return ans;
}ll solve
(ll x)
return
dfs(pos,0,
0,true)
.sum2%mod;
}int
main()
}}
基礎演算法之動態規劃 數字DP
數字dp一般用來統計乙個區間 l,r l,r l,r 中滿足條件f i f i f i 的數的個數。條件f i 條件 f i 條件f i 一般與數的大小無關,而與數的組成有關 即數字,個位 十位 百位 因此數的大小對複雜度的影響很小。數字dp本質是對暴力列舉的優化,使得新的列舉方式滿足dp性質,從而...
模板 動態規劃 數字dp
includeusing namespace std define ll long long int a 20 ll dp 20 20 可能需要的狀態1 20 可能需要的狀態2 不同題目狀態不同 ll dfs int pos,int state1 可能需要的狀態1 int state2 可能需要的狀...
動態規劃 數字dp入門(二)
較為簡單的數字dp只會涉及到每一位上的數字變化,如比較相鄰數字差,是否含有某個數字等等,在這種情況下一般用dp i j 就可以,i表示數字長度,j用來表示首位數字。如果題目要求對數字整體進行考慮,我們不能對各個位置上的數直接判斷,就需要對每位之前判斷過的數進行記憶化儲存。題目hdu3652 numb...