八段第四课:数位DP
A包含49的数字
解法
我们可以使用记忆化搜索从高到低一位一位放,每位都从 $0-9$ 枚举,使用数组优化。
设 $f_{i,0/1/2}$ 表示从高到低第 $i$ 为的三种情况:
- $0$ ,表示前面出现过 $49$.
- $1$,表示前面没出现过 $49$。
- $2$,表示前面没出现过 $49$,但是以 $4$ 结尾。
那么我们就可以直接转移。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include <bits/stdc++.h> using namespace std; #define int long long
long long n; long long dp[123][123]; int w[123]; long long dfs(int pos,int mask,int limt) { if(pos==0) return mask==0; if(!limt && dp[pos][mask]!=-1) return dp[pos][mask]; int x=(limt==1?w[pos]:9); long long ret=0; for(int i=0;i<=x;i++) { int nm=mask; if (mask==0) nm=0; else if (i==4) nm=2; else if (mask==2 && i==9) nm=0; else nm=1; ret+=dfs(pos-1,nm,limt&(i==x)); } if(!limt) dp[pos][mask]=ret; return ret; } void solve(int u) { int tot=0; while(u>0) w[++tot]=u%10,u/=10; int l=1,r=tot;
cout<<dfs(tot,1,1)<<endl; } signed main() { while(scanf("%lld",&n)==1) { memset(dp,-1,sizeof dp); solve(n); } }
|
B 书面零计数
解法
可以使用数位 $dp$。
跟上题一样,我们可以使用记忆化搜索。设 $f_{i,j}$ 表示已经填了 $i$ 位,前面出现了 $j$ 个 $0$ 的方案数。
然后直接枚举就行了,注意还需要一个变量判断目前是否是前导零。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <bits/stdc++.h> using namespace std; #define int long long int n,m; int w[33]; int dp[33][33];
int dfs(int pos,int lim,int cnt,int qdl) { if(pos==0) return cnt; if(dp[pos][cnt]!=-1 && !qdl && !lim) return dp[pos][cnt]; int x=lim?w[pos]:9; int ret=0; for(int i=0;i<=x;i++) { int t=cnt; if(i==0 && !qdl) t++; ret+=dfs(pos-1,lim&&(i==x),t,qdl&&(i==0)); } if(!lim && !qdl) dp[pos][cnt]=ret; return ret; } int solve(int n) { if(n==-1) return -1; memset(dp,-1,sizeof dp); int tot=0; while(n>0) w[++tot]=n%10,n/=10; return dfs(tot,1,0,1); } signed main() { scanf("%lld%lld",&n,&m); cout<<solve(m)-solve(n-1)<<endl; }
|