数位dp(D - How Many Zeroes? LightOJ - 1140 )

题目链接: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
#include
#include
#include
#include
#include
#include
#include
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;
}

 

你可能感兴趣的:(数位dp)