poj 3286 How many 0's? (数位DP+ 统计)

题意  :计算 m到n区间中的数一共包含了多少个0.

思路:定义DP状态dp[a][b][c]表示a位数,开头为b ,含有c个0的数的个数。

转移状态:

if(b>=1)

dp[a][b][c]+=dp[a-1][z][c]   (0=<z<=9)

else

       dp[a][b][c]+=dp[a-1][z][c-1]  (0=<z<=9)

对数字进行DP预处理,然后对于每个低于n或m的数字进行去除前导0的特殊处理,最后特判m即可。

本题WA在了两个地方 :

1、1000-1050   这里面如果直接算小于第三位数字的数那就直接跳过了,但实际上要记录,下一位往后的数字大小等量的0.

2、0 0这种情况要进行特判。

3、因为无符号整型,所以要用long long 。

#include <iostream>
#include <cstdio>
#include <cstring>
typedef long long LL;
using namespace std;
LL dp[22][12][22];
int len[3];
int num[3][22];
LL bit[19];//预处理10的几次方
LL nn[19];//处理m的i位往后数字组成的数是多少
LL nn1[19];//处理n的i位往后的数字组成的数是多少
LL m,n;
void work()
{
    dp[0][0][0]=1;
    for(int i=1; i<=20; i++)
    {
        for(int j=0; j<=9; j++)
            for(int k=0; k<=i; k++)
            {
                for(int z=0; z<=9; z++)
                {
                    if(j>=1)
                    {
                        dp[i][j][k]+=dp[i-1][z][k];
                    }
                    else
                    {
                       // if(k>=1)
                        dp[i][j][k]+=dp[i-1][z][k-1];
                    }
                }
            }
    }
}
void cal(int t)
{
    len[t]=0;
    if(t==0)
    {
        while(m)
        {
            //cout<<m<<endl;
            num[t][++len[t]]=m%10;
            nn[len[t]]=nn[len[t]-1]+num[t][len[t]]*bit[len[t]];
            m/=10;
        }
    }
    else
    {
        while(n)
        {
            num[t][++len[t]]=n%10;
            nn1[len[t]]=nn1[len[t]-1]+num[t][len[t]]*bit[len[t]];
            n/=10;
        }
    }
}
LL solve()
{
    LL res=0;
    LL sum=0;
    for(int i=len[0]; i>0; i--)
    {
        if(num[0][i]!=0)
        {
            res+=(nn[i])*sum;
            sum=0;
        }
        else
        {
            sum++;
        }
        for(int j=0; j<num[0][i]; j++)
        {
            if((j==0)&&(i==len[0])) continue;
            for(int l=0; l<=i; l++)
            {
                res+=(LL)dp[i][j][l]*l;
            }
        }
    }
    for(int i=len[0]-1; i>0; i--)
    {
        for(int j=1; j<=9; j++)
        {
            for(int z=0; z<i; z++)
            {
                res+=(LL)dp[i][j][z]*z;
            }
        }
    }
    LL res1=0;
    LL sum1=0;
    for(int i=len[1]; i>0; i--)
    {
        if(num[1][i]!=0)
        {
            res1+=(nn1[i])*sum1;//这里处理1050这种情况
            sum1=0;
        }
        else
        {
            sum1++;
        }
        for(int j=0; j<num[1][i]; j++)
        {
            if((j==0)&&(i==len[1])) continue;
            for(int l=0; l<=i; l++)
            {
                res1+=(LL)dp[i][j][l]*l;
            }
        }
    }
    for(int i=len[1]-1; i>0; i--)//特殊处理小于len的 
    {
        for(int j=1; j<=9; j++)
        {
            for(int z=0; z<i; z++)
            {
                res1+=(LL)dp[i][j][z]*z;
            }
        }
    }
    return res1-res;
}
LL check()
{
    LL cnt=0;
    for(int i=1; i<=len[1]; i++)
    {
        if(num[1][i]==0)
        {
            cnt++;
        }
    }
    return cnt;
}
int main()
{
    freopen("in.txt","r",stdin);
    memset(dp,0,sizeof(dp));
    memset(num,0,sizeof(num));
    memset(nn,0,sizeof(nn));
    memset(nn1,0,sizeof(nn1));
    work();
   bit[1]=1;
    for(int i=2;i<=17;i++)
    {
        bit[i]=bit[i-1]*10;
    }
    while(~scanf("%lld%lld",&m,&n))
    {
        LL ans1=0;
        if((m==0)&&(n==0))
        {
            printf("1\n");
            continue;
        }
        if((m==-1)) break;
        if(m==0)
        {
            num[0][1]=0;
            len[0]=1;
            ans1++;
        }
        else
        {
            cal(0);
        }
        cal(1);
        ans1+=solve()+check();
        printf("%lld\n",ans1);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值