數位dp
定義
數位dp(Digit Entry DP)是一種計數用的dp,一般就是要哦統計區間[l,r]
內滿足一些條件的數的個數。所謂數位dp,字面意思就是在數位上進行dp。數位的含義:一個數有個位、十位、百位、千位......數的每一位就是數位啦!
數位dp的思想
數位dp的實質就是換一種暴力枚舉的方式,使得新的枚舉方式滿足dp的性質,然后記憶化就可以了。
模板及例題
模板:
typedef long long ll;
int a[20];
ll dp[20][state]; //不同題目狀態不同
ll dfs(int pos,int state,bool lead,bool limit) //變量,狀態,前導0,數位上界;注意不是每題都要判斷前導零
{
if(pos==0) return 1; //遞歸邊界,一般一種遞歸到結束只能產生一種情況
if(!limit && !lead && dp[pos][state]!=-1) return dp[pos][state]; //記憶化
int up=limit?a[pos]:9; //枚舉上界
ll ans=0; //計數
for(int i=0;i<=up;i++) //枚舉,然后把不同情況的個數加到ans就可以了
{
if() ...
else if()... //一下條件
ans+=dfs(pos-1,/*狀態轉移*/,lead && i==0,limit && i==a[pos]) //最后兩個變量傳參都是這樣寫的
//state狀態轉移要保證i的合法性,比如不能有62,那么當pre==6&&i==2就不合法,這里用state記錄pre是否為6即可。
}
if(!limit && !lead) dp[pos][state]=ans;
return ans;
}
ll solve(ll x)
{
int tot=0;
while(x)
{
a[++tot]=x%10;
x/=10;
}
return dfs(tot/*從最高位開始枚舉*/,/*一系列狀態 */,true,true);//剛開始最高位都是有限制並且有前導零的,顯然比最高位還要高的一位視為0嘛
}
int main()
{
ll le,ri;
while(~scanf("%lld%lld",&le,&ri))
{
//初始化dp數組為-1,這里還有更加優美的優化,后面講
printf("%lld\n",solve(ri)-solve(le-1));
}
}
例題1:【不要62】(數位dp入門經典題)
描述:給定一個區間,不帶4以及沒有連續62的數字有多少個;
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[20][2],arr[20];
ll dfs(int pos,int state,bool lead,bool limit)
{
if(pos==0) return 1;
if(!limit && !lead && dp[pos][state]!=-1) return dp[pos][state];
int up = limit?arr[pos]:9;
ll ans = 0;
for(int i=0;i<=up;++i)
{
if(i==4) continue;
if(state && i==2) continue;
ans += dfs(pos-1,i == 6,lead && i==0,limit && i==arr[pos]);
}
if(!limit && !lead) dp[pos][state] = ans;
return ans;
}
ll solve(int x)
{
int tot = 0;
while (x){
arr[++tot]=x%10;
x/=10;
}
return dfs(tot,0,true,true);
}
int main()
{
int l,r;
while(scanf("%d %d",&l, &r) && (l||r))
{
memset(dp,-1,sizeof(dp));
printf("%lld\n",solve(r) - solve(l-1));
}
system("pasue");
}