Lightoj 1140(数位DP)
求一个区间内的数含有多少个0.
dp[len][pre]表示长度为len的数,含有pre个0.
需要加一个标记,来表示前缀是否为0(可以是一串连续的0),如果前缀一直为0,就一直搜,如果前缀不为0,就可以用到dp[len-1][pre+1]或者dp[len-1][pre]
了,如果前缀的最后一位是0,就是dp[len-1][pre+1],如果前缀的最后一位不是0,就是dp[len-1][pre],当然了第一次肯定是需要先搜的.
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> using namespace std; #define LL long long #define maxn 30 LL dp[maxn][maxn];//代表长度为len的数字,有多少个0 LL digit[maxn]; int LEN; LL dfs(int len,LL pre,int flag,bool fp) //dfs版本的纯属暴力枚举每一个数字,而递推版本的是考虑了前缀的影响 { if(len==0) { if(flag) return 1; else return pre; } if(!fp && dp[len][pre] != -1 && !flag) //如果之前的数字不全是0,可以直接用 { return dp[len][pre]; } LL ret =0; int fpmax = fp ? digit[len] : 9; for(int i=0;i<=fpmax;i++) //分别算出以i开头的数的方案数, { LL temp=0; if(flag) //如果之前一直是0 { temp=dfs(len-1,pre,flag && (i==0),fp && i==fpmax); ret+=temp; } else { if(i==0) { temp=dfs(len-1,pre+1,flag,fp && i==fpmax); ret+=temp; } else { temp=dfs(len-1,pre,flag,fp && i==fpmax); ret+=temp; } } } if(!fp && !flag) //如果之前的数字不全是0 dp[len][pre]= ret; return ret; } LL f(LL n) { if(n==-1) return 0; int len=0; while(n) { digit[++len] = n % 10; n /= 10; } LL ans=0; // LEN=len; ans+=dfs(len,0,1,true); return ans; } void init() { memset(dp,-1,sizeof(dp)); } int main() { //freopen("test.txt","r",stdin); int t; scanf("%d",&t); int Case=0; while(t--) { init(); LL n,m; scanf("%lld%lld",&n,&m); LL ans1=f(m); // printf("%lld\n",ans1); init(); LL ans2=f(n-1); // printf("%lld\n",ans2); printf("Case %d: %lld\n",++Case,ans1-ans2); } return 0; }