AcWing 1086. 恨7不成妻(【程式碼簡潔】標準記憶化搜尋+超詳解!!)
阿新 • • 發佈:2021-07-20
看到這題用迴圈寫的dp程式碼瑟瑟發抖~
數位dp一般記憶化搜尋的寫法思維難度較低,也比較常用,這題的簡潔程式碼應該就可以顯現出其優越性
(用時4ms,可能比用迴圈寫的dp還要快)
那這裡補充一下記憶化搜尋的寫法叭qwq保姆式超詳細講解哦
有註釋程式碼
#include<iostream> #include<cstring> using namespace std; typedef long long ll; const ll P=1e9+7; int A[25]; ll pw[25]; struct node {ll cnt,sum,sum2;} f[20][7][7]; /* f[I][sum][num]表示的狀態為: 從第I位開始,最高位到第I位每位數字之和(%P)為sum,整個數字(%P)為num 如對於數123***,I=3時,sum=6,num=123 注:f儲存的是在沒有貼合上界的情況下 因為沒有貼合上界,即剩下i位可以從00…00~99…99隨便填,所以無論數a[]是多少都可以適用,不需要每次都重置f陣列 在該狀態下,結構體中的 cnt表示與7無關的數的個數 sum表示所有與7無關的數的和 num表示所有與7無關的數的平方和 */ node dfs(int I,int sum,int num,bool lim){ //當前在第I位,最高位到第I位每位數字之和(%P)為sum,整個數字(%P)為num,lim表示是否貼合上界 if (!I) return (node){sum && num , 0 , 0}; //數字已填完,根據題目要求,若sum和num都不為0(不能被7整除),則算一種方案 if (!lim && f[I][sum][num].cnt>=0) return f[I][sum][num]; //記憶化,如果不貼合上界(!lim),直接放回記錄過的答案 int up=lim ? A[I] : 9; //第I位最大能填的數 node ans=(node){0,0,0}; for (int i=0 ; i<=up ; i++) //列舉第I位填的數 if (i!=7){ node J=dfs(I-1,(sum+i)%7,(num*10+i)%7,lim && i==up); ll B=i*pw[I-1]; //B可以理解為當前層的基值,例如第I=5位填6,則B=60000 (ans.cnt+=J.cnt)%=P; //統計與7無關數出現次數 (ans.sum+=J.cnt*B+J.sum)%=P; /* 統計所有與7無關數的和(用dfs(I-1)已經求出了所有無關數第I-1位到最後一位所組成的數之和,即J.sum,再加上第I位即可,即J.cnt*B) 例如I=5,已知無關數有**61111,**62222,**63333(隨便瞎寫的幾個數字) 則B=60000,J.sum=1111+2222+3333,J.cnt=3,ans.sum=61111+62222+63333 */ (ans.sum2+=J.cnt*B%P*B%P+J.sum2+2*J.sum%P*B%P)%=P; /* 統計所有與7無關數第I位到最後一位所組成的數的平方和 例如I=5,已知無關數有**61111,**62222,**63333(隨便瞎寫的幾個數字) 對於61111^2=(60000+1111)^2=(60000)^2+(1111)^2+2*60000*1111 62222,63333同理 則ans.sum2=61111^2+62222^2+63333^2 =3*(60000)^2 + (1111^2+2222^2+3333^2) + 2*60000*(1111+2222+3333) =J.cnt*B*B + J.sum2 + 2*B*J.sum 可以發現,我們用後I-1位的sum2即可推算出後I位的sum2 */ } if (!lim) f[I][sum][num]=ans; //記憶化:如果不貼合上界(!lim),則記錄 return ans; } ll solve (ll X){ //分解數位 int len=0; for ( ; X ; X/=10) A[++len]=X%10; return dfs(len,0,0,1).sum2; } int main(){ int T; cin>>T,pw[0]=1,memset(f,-1,sizeof f); for(int i=1 ; i<21 ; i++) pw[i]=pw[i-1]*10%P; //預處理10的冪 for (ll L,R ; T ; T--) scanf("%lld%lld",&L,&R),printf("%lld\n",(solve(R)-solve(L-1)+P)%P); }
無註解程式碼
#include<iostream> #include<cstring> using namespace std; typedef long long ll; const ll P=1e9+7; int A[25]; ll pw[25]; struct node {ll cnt,sum,sum2;} f[20][7][7]; node dfs(int I,int sum,int num,bool lim){ if (!I) return (node){sum && num , 0 , 0}; if (!lim && f[I][sum][num].cnt>=0) return f[I][sum][num]; int up=lim ? A[I] : 9; node ans=(node){0,0,0}; for (int i=0 ; i<=up ; i++) if (i!=7){ node J=dfs(I-1,(sum+i)%7,(num*10+i)%7,lim && i==up); ll B=i*pw[I-1]; (ans.cnt+=J.cnt)%=P; (ans.sum+=J.cnt*B+J.sum)%=P; (ans.sum2+=J.cnt*B%P*B%P+J.sum2+2*J.sum%P*B%P)%=P; } if (!lim) f[I][sum][num]=ans; return ans; } ll solve (ll X){ int len=0; for ( ; X ; X/=10) A[++len]=X%10; return dfs(len,0,0,1).sum2; } int main(){ int T; cin>>T,pw[0]=1,memset(f,-1,sizeof f); for(int i=1 ; i<21 ; i++) pw[i]=pw[i-1]*10%P; for (ll L,R ; T ; T--) scanf("%lld%lld",&L,&R),printf("%lld\n",(solve(R)-solve(L-1)+P)%P); }
有問題的話歡迎在評論區留言哦~