题目链接:https://cn.vjudge.net/contest/278036#problem/D
题目大意:T组测试数据,每一次输入两个数,求的是在这个区间里面,有多少个0,比如说19203包括一个0,123包括0个0。
具体思路:数位dp,对于当前的这一位的所有情况,先看一下这一位上之前的数是不是都是0,如果都是0的话,那么这一位上即使是0也不能计算在内,因为00还是1个0。dp[i][j]代表的是第i位之前有多少0,注意,对于初始条件,我们设置为这个数的前面也都是0,举个例子1234,我们在枚举第一位的所有情况的时候,如果是0的话,是不应该记录在内的,所以我们设置初始位置也存在前导0.
AC代码:
#include<iostream>
#include<stack>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<stdio.h>
#include<algorithm>
#include<string>
using namespace std;
# define ll long long
const int maxn =10+10;
ll dig[maxn],dp[maxn][maxn];
ll dfs(int len,int t,bool head0,bool fp)
{
if(!len)
{
if(head0)//一开始在这个地方卡住了,我们需要记录的是第i位之前存在多少0,那么0的时候就是一个,1-9也是1个。
return 1;
return t;
}
if(!fp&&!head0&&dp[len][t]!=-1)
return dp[len][t];
ll ans=0,fmax = fp?dig[len]:9;
for(int i=0; i<=fmax; i++)
{
if(head0)
ans+=dfs(len-1,0,head0&&i==0,fp&&i==fmax);//按照递归的形式,只有当前面的都是0的时候,当前这一位也是0的时候,才算是前导0
else
ans+=dfs(len-1,t+(i==0),head0&&i==0,fp&&i==fmax);
}
if(!fp&&!head0)
dp[len][t]=ans;
return ans;
}
ll cal(ll t)
{
int num=0;
memset(dp,-1,sizeof(dp));
while(t)
{
dig[++num]=t%10;
t/=10;
}
return dfs(num,0,1,1);
}
int main()
{
int T;
scanf("%d",&T);
int Case=0;
while(T--)
{
ll n,m;
scanf("%lld %lld",&n,&m);
printf("Case %d: ",++Case);
printf("%lld\n",cal(m)-cal(n-1));
}
return 0;
}