1. 程式人生 > >模板 - 動態規劃 - 數位dp

模板 - 動態規劃 - 數位dp

也不能 需要 limit 更新 返回 bsp size int 完全

#include<bits/stdc++.h>
using 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/*可能需要的狀態2*/,bool lead/*這一位的前面是否為零*/,bool limit/*這一位是否取值被限制(也就是上一位沒有解除限制)*/)
//不是每個題都要處理前導零
{
    //遞歸邊界,最低位是0,那麽pos==-1說明這個數枚舉完了
if(pos==-1) return 1;/*這裏返回1,表示枚舉的這個數是合法的,那麽這裏就需要在枚舉時必須每一位都要滿足題目條件,也就是說當前枚舉到pos位,一定要保證前面已經枚舉的數位是合法的。 */ //第二個就是記憶化(在此前可能不同題目還能有一些剪枝) if(!limit && !lead && dp[pos][state1][state2]!=-1) return dp[pos][state1][state2]; /*常規寫法都是在沒有限制的條件記憶化,這裏與下面記錄狀態對應*/ int
up=limit?a[pos]:9;//根據limit判斷枚舉的上界up ll ans=0; //開始計數 for(int i=0;i<=up;i++)//枚舉,然後把不同情況的個數加到ans就可以了 { int new_state1=???; int new_state2=???; /* 計數的時候用continue跳過不合法的狀態,不再搜索 */ //合法的狀態向下搜索 ans+=dfs(pos-1,new_state1,new_state2,lead && i==0
,limit && i==a[pos]);//最後兩個變量傳參都是這樣寫的 } //計算完,記錄狀態 if(!limit && !lead) dp[pos][state1][state2]=ans; /*這裏對應上面的記憶化,在一定條件下時記錄,保證一致性,當然如果約束條件不需要考慮lead,這裏就是lead就完全不用考慮了*/ return ans; } ll solve(ll x) { //可能需要特殊處理0或者-1 if(x<=0) return ???; int pos=0; while(x)//把數位分解 { a[pos++]=x%10;//編號為[0,pos),註意數位邊界 x/=10; } return dfs(pos-1/*從最高位開始枚舉*/,0/*可能需要的狀態1*/,0/*可能需要的狀態2*/,true,true);//剛開始最高位都是有限制並且有前導零的,顯然比最高位還要高的一位視為0嘛 } int main() { memset(dp,-1,sizeof(dp)); //一定要初始化為-1 ll le,ri; while(~scanf("%lld%lld",&le,&ri)) { printf("%lld\n",solve(ri)-solve(le-1)); } }

其實另一種計數寫法對別的題目有一定的啟發性,需要特別註意的是,無論哪種寫法的dp結果中存的數字都是和le與ri無關的。所以在數位受限時不能取用計算過的dp值,也不能更新dp值,不受限的情況可以重復利用。


一個更簡單的模板,去掉了很多奇奇怪怪的東西,比如前導0,前導0的確應該特殊考慮而不能一概而論。

int dfs(int i, int s, bool e) {
    if (i==-1) return s==target_s;
    if (!e && ~f[i][s]) return f[i][s];
    int res = 0;
    int u = e?num[i]:9;
    for (int d = first?1:0; d <= u; ++d)
        res += dfs(i-1, new_s(s, d), e&&d==u);
    return e?res:f[i][s]=res;
}

看起來清爽多了,其中:

f為記憶化數組;

i為當前處理串的第i位(權重表示法,也即後面剩下i+1位待填數);

s為之前數字的狀態(如果要求後面的數滿足什麽狀態,也可以再記一個目標狀態t之類,for的時候枚舉下t);

e表示之前的數是否是上界的前綴(即後面的數能否任意填)。

for循環枚舉數字時,要註意是否能枚舉0,以及0對於狀態的影響,有的題目前導0和中間的0是等價的,但有的不是,對於後者可以在dfs時再加一個狀態變量z,表示前面是否全部是前導0,也可以看是否是首位,然後外面統計時候枚舉一下位數。

註意:

不滿足區間減法性質的話,不能用solve(r)-solve(l-1)。

模板 - 動態規劃 - 數位dp