數位dp

數位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也須要記的!!! 

相關文章
相關標籤/搜索