不了解dp的可以先看一下dp
數位dp含義:
數位:一個數有個位,十位,百位,千位等等,數的每一位都是數位。
數位dp歸為計數dp,是在數位上進行操作的dp。
數位dp的實質是一種快速枚舉的方式,它滿足dp的性質,然后進行記憶化搜索。
用途:
有兩個數,兩個數范圍很大(例如1e9,甚至更大),求這兩個數符合限定條件的個數。純暴力不行,就要用數位dp。
例子:求從0到n,(n為2^32-1),(條件)求包含49的數有多少;
思路or具體實現:
n為2^32-1,數位其實只有20位,枚舉數位,就不會超時。
dp[shuwei][diaojian]。dp的第一維通常是數位,后面的幾維根據題目條件來設定。上面給的例子只用了一維。
控制上界枚舉,從最高位往下枚舉。用記憶化搜索來做,拋開循環后轉移狀態能更加隨意,大部分數位和動態規化的題都可隨意切換。搜索與循環異曲同工之妙,但前者更易轉移狀態,在限制較多的情況下被大部分人喜愛。
例題:Bomb
思路 :數位dp=dfs+記憶化搜索。
需要注意上限即題目所給范圍的預處理,本題用digtis[20] 數組,存儲上限的數位,最好用一個函數來處理,比如solve(sum),處理時,對上限的數位總數拿一個變量進行存儲,比如k或len。
在dfs中用 limit (有些題解是top)判定上限,dfs(len,條件,limit);
dfs執行數位dp,在搜索時用了 up_bound=(limit?digit[len]:9); 來標記上限,同時用cnt來存儲滿足的條件的數量,然后更新dp數組,更新時要滿足if(!limit),到達上界,狀態不完整。
在dfs中對條件的處理,需要根據題意去確定,每個題目不一樣。
是否頂着上界,每層確定這一位選啥,判斷是否和上一位沖突,全部確定完了方案數+1。
由於頂着上界是比較特殊的情況,所以這類答案直接一層層搜索出答案,不用記憶化,其他(不頂着上界)的情況用dp[][] 直接返回數量。
有些題目需要最后算一下最高位為0的情況。

#include<bits/stdc++.h> using namespace std; typedef long long LL; int digit[20]; //儲存上界的每個數位 LL dp[20][2]; //統計沒有49的總數 //if4它的上一位和當前位是否是4 //len 記錄當前數位,從高位往下搜索 //limit 上一位是否是上界 LL dfs(int len, bool if4, bool limit){ if(len==0) return 1ll; //個位的時候,有一個分支。 if(!limit && dp[len][if4]) return dp[len][if4]; //沒到達上界並且數位已經統計過,直接返回數量。 LL cnt=0,up_bound=(limit?digit[len]:9); //標記數位的上界 //對整個數位進行記憶化搜索 for(int i=0;i<=up_bound;++i){ if(if4&&i==9) continue;//碰到49不加入。 cnt+=dfs( len-1, i==4,limit && i==up_bound); //向下搜索,判斷上一位是否為4,上一位是否到達上界,當前位是否到上界。 } if(!limit) dp[len][if4]=cnt; //到達上界是狀態不完整,不更新dp return cnt;//直接返回本次搜索結果,加入到最后結果中 } LL solve(LL num) //num是上界這個數 { int k=0;//記錄數位個數。 while(num) { digit[++k]=num%10; num/=10; } return dfs(k,false,true); } int main(){ int t; cin>>t; while(t--) { LL n; cin>>n; cout<<n+1-solve(n)<<endl; } return 0; }