[Luogu P4124] [CQOI2016]手機號碼 (數位DP)
阿新 • • 發佈:2019-01-14
題面
傳送門:洛咕
Solution
感謝神仙@lizbaka的教學
這題是數位DP的非常非常模板的題目,只是狀態有點多
.
這題我使用記憶化搜尋實現的
中國有句古話說的好,有多少個要求就設多少個狀態。
所以說,考慮這樣設定狀態:
設\(f[i][j][k][2][2][2][2][2]\)表示當前填到第i位,上一位填了j,上兩位填了k,是否卡上界,上一個數是否為前導零,是否有4,是否有8,是否出現了連續三個相同的數字,之後任意填的可行方案總數
使用記憶化搜尋的話,轉移是非常容易的,我們只需要像寫搜尋一樣遞迴寫下去就好
差不多長成這樣:
for(int i=0;i<=(limit==true?l[to]:9);i++) { if(zero==false or i!=0) t_ans+=dfs(to+1,i,last1,limit==true and i==l[to],false,four or i==4,eight or i==8,ok==true or(last1==last2 and last2==i)); else t_ans+=dfs(to+1,i,last1,limit==true and i==l[to],true,false,false,false); }
注:此題不用討論前導零,但是我為了模板的完整性,也加上了。
注意,遞迴演算法一定要有出口,這裡也不例外,出口為i==n+1(即已經填完了)
具體寫法還請看程式碼
Code
//Luogu P4124 [CQOI2016]手機號碼 //Jan,13rd,2019 //測試遞迴實現數位DP #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=20; long long f[N][15][15][2][2][2][2][2];//位數,上一位,上兩位,limit,zero,4,8,OK int n,l[N]; long long dfs(int to,int last1,int last2,bool limit,bool zero,bool four,bool eight,bool ok) { if(f[to][last1][last2][limit][zero][four][eight][ok]>=0) return f[to][last1][last2][limit][zero][four][eight][ok]; long long t_ans=0; if(to==n+1) { if((four and eight)==false and ok==true) t_ans=1; return f[to][last1][last2][limit][zero][four][eight][ok]=t_ans; } for(int i=0;i<=(limit==true?l[to]:9);i++) { if(zero==false or i!=0) t_ans+=dfs(to+1,i,last1,limit==true and i==l[to],false,four or i==4,eight or i==8,ok==true or(last1==last2 and last2==i)); else t_ans+=dfs(to+1,i,last1,limit==true and i==l[to],true,false,false,false); } return f[to][last1][last2][limit][zero][four][eight][ok]=t_ans; } int main() { long long ans[3]; for(int i=1;i<=2;i++) { long long t_num; scanf("%lld",&t_num); if(i==1) t_num--; n=0; while(t_num!=0) l[++n]=t_num%10,t_num/=10; reverse(l+1,l+1+n); memset(f,0x80,sizeof f); dfs(1,0,0,true,true,false,false,false); ans[i]=f[1][0][0][true][true][false][false][false]; } printf("%lld",ans[2]-ans[1]); return 0; }