#includeusingnamespace
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/*
這一位是否取值被限制(也就是上一位沒有解除限制)*/)
//不是每個題都要處理前導零
//計算完,記錄狀態
if(!limit && !lead)
dp[pos][state1][state2]=ans;
/*這裡對應上面的記憶化,在一定條件下時記錄,保證一致性,當然如果約束條件不需要考慮lead,這裡就是lead就完全不用考慮了
*/return
ans;
}ll solve(ll x)
return dfs(pos-1
/*從最高位開始列舉
*/,0
/*可能需要的狀態1
*/,0
/*可能需要的狀態2
*/,true,true);//
剛開始最高位都是有限制並且有前導零的,顯然比最高位還要高的一位視為0嘛
}int
main()
}
其實另一種計數寫法對別的題目有一定的啟發性,需要特別注意的是,無論哪種寫法的dp結果中存的數字都是和le與ri無關的。所以在數字受限時不能取用計算過的dp值,也不能更新dp值,不受限的情況可以重複利用。
#includeusingnamespace
std;
#define ll long long
int a[20
];ll dp[
20][maxs1][maxs2];
ll dfs(
int pos,int s1,int s2,bool lead,bool
limit)
if(!limit && !lead && dp[pos][s1][s2]!=-1
)
return
dp[pos][s1][s2];
int up=limit?a[pos]:9
; ll ans=0
;
for(int i=0; i<=up; i++)
if(!limit && !lead)
dp[pos][s1][s2]=ans;
return
ans;
}ll solve(ll x)
return dfs(pos-1,inits1,inits2,true,true);}
intmain()
}
乙個更簡單的模板,去掉了很多奇奇怪怪的東西,比如前導0,前導0的確應該特殊考慮而不能一概而論。
int dfs(int i, int s, boole)
看起來清爽多了,其中:
f為記憶化陣列;
i為當前處理串的第i位(權重表示法,也即後面剩下i+1位待填數);
s為之前數字的狀態(如果要求後面的數滿足什麼狀態,也可以再記乙個目標狀態t之類,for的時候列舉下t);
e表示之前的數是否是上界的字首(即後面的數能否任意填)。
for迴圈列舉數字時,要注意是否能列舉0,以及0對於狀態的影響,有的題目前導0和中間的0是等價的,但有的不是,對於後者可以在dfs時再加乙個狀態變數z,表示前面是否全部是前導0,也可以看是否是首位,然後外面統計時候列舉一下位數。
注意:
不滿足區間減法性質的話,不能用solve(r)-solve(l-1)。
看了學長的部分部落格之後發現其實使用f[i][j][st]表示以j開頭的i位數滿足條件st的數的個數也是可以的。待更新。
動態規劃之數字dp
數字dp,字面意思理解就是在數字的每一位上面去dp,動態規劃一般有兩種 遞推,記憶化搜尋 dfs 這裡就是用的記憶化。一般這種用在計數上面,對那些數字上面有限制的計數。這裡上一道模板題 題中就是要你統計1 n裡有 49 的個數。dp pos sta 表示到第pos位,狀態為sta的總數。我們一般是從...
模板 動態規劃 區間dp
因為昨天在codeforces上設計的區間dp錯了 錯過了上紫的機會 覺得很難受。看看學長好像也有學,就不用看別的神犇的了。區間dp處理環的時候可以把序列延長一倍。for int len 1 len n len 首先,使用四邊形優化要滿足下面的性質 當小區間包含在大區間中,則小區間的成本不高於大區間...
動態規劃 數字dp入門(二)
較為簡單的數字dp只會涉及到每一位上的數字變化,如比較相鄰數字差,是否含有某個數字等等,在這種情況下一般用dp i j 就可以,i表示數字長度,j用來表示首位數字。如果題目要求對數字整體進行考慮,我們不能對各個位置上的數直接判斷,就需要對每位之前判斷過的數進行記憶化儲存。題目hdu3652 numb...