ZJOI2010 數字計數【數位dp】
阿新 • • 發佈:2020-12-04
題目解析
稍微有點難度的數位\(dp\)
這道題的話,你發現前導零需要特判一下,不然你就會把前導零數到\(0\)的個數裡面去。
然後就是狀態定義的不同 這道題把計數\(sum\)也放在記憶化數組裡面了,理由詳見程式碼註釋。
►Code View
#include<cstdio> #include<algorithm> #include<queue> #include<cstring> using namespace std; #define N 15 #define MOD 1000000007 #define INF 0x3f3f3f3f3f3f3f3f #define LL long long LL rd() { LL x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();} return f*x; } int d[N],n=0; LL f[N][N][2][2];//f[i][j][m][p] 當前位數到i 當前數位dig有j個 後面兩維是最高位和前導零 一共有多少個dig /* 關於狀態為什麼要記錄當前狀態表示出來的數的dig個數 可以想一下如果去掉會怎麼樣 或者是想一下不同的sum之間有無區別 因為是記憶化嘛 發現是有區別的 因為在dp的時候 這個狀態會拿去更新別人 而sum不同代表你前面枚到的數不一樣 那麼你拿去更新別人的時候 對別人的sum的貢獻不一樣 如果全部混為一談 答案會算多 而dp值是相當於整個dfs樹的子樹答案和 也就是做到這一位的答案總數 */ LL dfs(int dig,int p,int ld,int s,int sum)//有前導零ld=1 { if(p==0) return sum; if(f[p][sum][ld][s]!=-1) return f[p][sum][ld][s]; LL res=0; int lim=9; if(s==1) lim=d[p]; for(int i=0;i<=lim;i++) { int del=0; if(i==0&&dig==0&&ld) del=0; else if(i==dig) del=1; res+=dfs(dig,p-1,ld&(i==0),s&(i==d[p]),sum+del); } return f[p][sum][ld][s]=res; } LL solve(LL x,int dig) { n=0; while(x) { d[++n]=x%10; x/=10; } memset(f,-1,sizeof(f)); return dfs(dig,n,1,1,0); } int main() { LL a=rd(),b=rd(); for(int i=0;i<=9;i++) printf("%lld ",solve(b,i)-solve(a-1,i)); return 0; }