LightOJ 1140: How Many Zeroes? (数位DP)
当前数位DP还不理解的点:
1:出口用i==0的方式
2:如何省略状态d(就是枚举下一个数的那个状态。当然枚举还是要的,怎么把空间省了)
总结:
1:此类DP,考虑转移的时候,应当同时考虑查询时候的情况。
2:考虑x在第i位之后,能遍历多少数字,其答案为(x%10i-1+1)
3:这里的记忆化搜索不太一样喔,出口一定要写在递归里,不然,查询状态下差到出口就会出错了~
类型:
数位DP
题意:
求[A,B]区间内的所有数,写下来之后,0的个数。(a,b 为 unsigned int)
思路:
我的笨拙暴力状态:
dp[i][d][okPre] 表示d开头的i位数,(okPre表示计算前导0的情况下,反之~),的0的个数。
那么。
dp[i][d][含] = dp[i-1][0~9(num[i-1])][含] + 10i-1(x%10i-1+1) * (d==0);
dp[i][d][不含] = dp[i-1][1~9(num[i-1])][含] + dp[i-1][0][d==0?不含:含] ;
出口:
dp[1][1~9][含] =dp[1][1~9][不含] = 0;
dp[1][0][不含] = dp[1][0][含] = 1;
当时确定出口的时候,不含的0应该是0还是1呢?不好确定,感觉是1,最后是通过试验确定的。
还是没有理解别人代码中 用 i==0 做出口 是怎么实现的。
我的代码:
#include <cstdio> #include <cstring> #include <iostream> using namespace std; long long nowx; long long dp[20][10][2]; int num[30]; long long dfs(int i, int d, bool okPreZero, bool isQuery) { //printf("(%d,%d,%s,%s)\n", i, d, okPreZero?"T":"F", isQuery?"T":"F"); long long &nowdp = dp[i][d][okPreZero]; if (!isQuery && ~nowdp) return nowdp; if (i == 1) { if (d!=0) return nowdp = 0; else if (okPreZero) { return nowdp = 1; } else { return nowdp = 1; } } long long ans = 0; int end = isQuery?num[i-1]:9; for (int j = 0; j <= end; j++) { if (okPreZero) { ans += dfs(i-1,j,true,isQuery && j==end); } else { if (d == 0 && j == 0) { ans += dfs(i-1,j,false,isQuery && j==end); } else { ans += dfs(i-1, j, true, isQuery && j==end); } } } long long ten = 1; for (int j = 0; j < i-1; j++) ten*=10; if (d==0 && okPreZero) ans += (isQuery?((nowx%ten)+1):(ten)); if (!isQuery) nowdp = ans; return ans; } long long cal(long long x) { nowx = x; if (x == -1) return 0; if (x == 0) return 1; int len = 0; while (x) { num[++len] = x%10; x/=10; } return dfs(len+1, 0, false, true); } int Nmain() { long long a; memset(dp, -1, sizeof(dp)); while (cin>>a) { cout<<"---"<<cal(a)<<endl; } return 0; } int main() { int t; cin>>t; int cas = 1; memset(dp, -1, sizeof(dp)); while (t--) { long long m, n; cin>>m>>n; cout<<"Case "<<cas++<<": "<<cal(n)-cal(m-1)<<endl; } return 0; }
不理解的代码:
#include<cstdio> #include<cstring> #include<cmath> typedef long long LL; LL dp[14][12][2]; int bit[14],len; LL a,b; LL dfs(int pos,int v,int flag,int limit) { if (pos<=0) return flag?v:0; if (!limit&&dp[pos][v][flag]!=-1) return dp[pos][v][flag]; int end=(limit?bit[pos]:9); LL re=0; for (int i=0;i<=end;i++) { int tmp; if (flag&&(i==0)) tmp=1;else tmp=0; re+=dfs(pos-1,v+tmp,flag||i,limit&&(end==i)); } if (!limit) dp[pos][v][flag]=re; return re; } LL solve(LL n) { if (n==-1) return -1; if (n==0) return 0; len=0; while (n) { bit[++len]=n%10; n/=10; } return dfs(len,0,0,1); } int main() { memset(dp,255,sizeof(dp)); int cas,i=0; scanf("%d",&cas); while (cas--) {scanf("%lld%lld",&a,&b); printf("Case %d: %lld\n",++i,solve(b)-solve(a-1)); } return 0; }
posted on 2014-03-13 21:52 ShineCheng 阅读(433) 评论(0) 编辑 收藏 举报