您的位置:首页 > 其它

Lightoj 1140(数位DP)

2015-08-22 16:03 477 查看
求一个区间内的数含有多少个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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: