數位DPc++
經典的數位Dp是要求統計符合限制的數字的個數。算法
通常的形式是:求區間[n,m]知足限制f(1)、f(2)、f(3)等等的數字的數量是多少。條件 f(i)通常與數的大小無關,而與數的組成有關。數組
善用不一樣進制來處理,通常問題都是10進制和二進制的數位dp。spa
數位dp的部分通常都是很套路的,可是有些題目在數位dp外面套了一個華麗的外衣,有時咱們難以看出來。code
例題:windy數blog
精華都在代碼的註釋裏呢,好好看吧it
代碼:ast
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll l,r; int num[20]; ll f[20][20];/*按照題目定維數*/ ll dfs(int i,int last,bool have,bool lim) /*須要傳遞的各類參數,視題目而定*/ /*本題中分別表示當前到了第幾位,上一位是什麼, 有沒有前導0,有沒有頂上界*/ /*通常須要有的是位數,上界,其餘視狀況而定*/ { if(i==0) return 1;/*判斷是否已經枚舉完,返回1就表示你枚舉的數是合法的*/ if(!have&&!lim&&f[i][last]!=-1)/*判斷邊界條件以及是否已經搜索過*/ return f[i][last]; int up=0;/*這一位枚舉的上界*/ if(lim==1) up=num[i];/*上一位頂到上界的話對這一位就有限制*/ else up=9;/*若是上一位沒有頂到上界的話這一位就能夠隨意*/ ll ans=0; int op=0;/*表示這一位選擇的數是什麼*/ for(int j=0;j<=up;j++) { if(abs(j-last)<2) continue;/*判斷不合法條件*/ op=j; if(have&&j==0) op=-2; ans+=dfs(i-1,op,op==-2,lim&&j==up);/*繼續記搜,必定要注意各類限制條件的傳遞*/ } if(!have&&!lim) f[i][last]=ans;/*在必定條件下記錄記搜的答案*/ return ans; } ll work(ll x) { memset(num,0,sizeof(num)); int tot=0; while(x)/*把數字的各個位拆到數組中存儲*/ { num[++tot]=x%10; x/=10; } memset(f,-1,sizeof(f)); return dfs(tot,-2,1,1); } int main() { scanf("%lld%lld",&l,&r); printf("%lld",work(r)-work(l-1));/*前綴和思想*/ }
來看另外一道題class
洛谷P4317 花神的數論題搜索
題意就是說求1~n每一個數二進制含有1的個數的乘積
也是數位DP
設f[i][j]表示前i位有j個1的方案數
而後記憶化搜索
代碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=10000007; ll n; ll f[51][51]; int num[51]; ll dfs(int i,int cnt,bool lim) { if(i==0) return max(cnt,1); if(!lim&&f[i][cnt]!=-1) return f[i][cnt]; ll ans=1; int up=0; if(lim==1) up=num[i]; else up=1; for(int j=0;j<=up;j++) { ans=(ans*dfs(i-1,j==1?cnt+1:cnt,lim&&j==up))%mod; } if(!lim&&f[i][cnt]==-1) f[i][cnt]=ans; return ans; } inline void work(ll x) { int cnt=0; while(x) { num[++cnt]=x%2; x/=2; } cout<<dfs(cnt,0,1); } int main() { scanf("%lld",&n); memset(f,-1,sizeof(f)); work(n); }
關於數位dp的經驗
1:注意不少時候帶進去是n==0要特殊處理。
2:還有通常問[m,n],咱們求[1,n]-[1,m-1]可是有的時候m爲0就炸了。
3:求全部包含49的數,其實就是(總數-全部不包含49的數)。前者的化須要有兩維限制,一個是上一位是什麼,一個是以前有沒有49。可是後 者只須要記一個上一位是什麼。就能好寫一些。
4:通常問題的數位dp部分,都是套路,可是這並不表明它外面「華麗的外衣」和與其餘算法結合的的部分也是無腦的。要看出它是考數位dp,要看出問題怎麼變化一下就是數位dp了。
5:dp初始化memset要置爲-1。不能置爲0!!!!!!由於有不少時候dp值就應該是0,而後咱們若是誤覺得是由於以前沒有計算,從新計算的話,就會tle。
6:既然是記憶化搜索,那就能夠剪枝!!!!可行性剪枝!!
7:注意windy數的狀況,有時前導0也須要記的!!!