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

来源:互联网 发布:网络打鱼能赢吗 编辑:程序博客网 时间:2024/06/03 13:29

题意  :计算 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;}


原创粉丝点击