HDU 3555 Bomb (数位DP)

判断(0,n]之间含49的数的个数

具体做法是数位DP , 用3个DP数组分别记录第i位

dp0[i];//不含49的数的个数
dp1[i];//不含49,但第一位是9的个数
dp2[i];//含49个数

则由状态转移方程

        dp0[i+1]=dp0[i]*10-dp1[i];
        dp1[i+1]=dp0[i];
        dp2[i+1]=dp1[i]+dp2[i]*10;

初始为1 0 0 就可以

之后处理个数,按字符串读入之后按位处理,每次递加比该数位小的应有的数,所有如果n本身就是含49的话,就要再+1。

 

#include <cstdio>
#include <cstring>

typedef long long ll;
const int maxn=25;

ll dp0[maxn];//不含49
ll dp1[maxn];//不含49,但第一位是9的
ll dp2[maxn];//含49

void init ()
{
    dp0[0]=1;
    dp1[0]=0;
    dp2[0]=0;
    for (int i=0 ; i<20 ; ++i)
    {
        dp0[i+1]=dp0[i]*10-dp1[i];
        dp1[i+1]=dp0[i];
        dp2[i+1]=dp1[i]+dp2[i]*10;// dp2[i]*10表示dp2[i]*9+dp2[i]好理解些
        //printf("%lld  %lld  %lld  %lld\n",dp0[i],dp1[i],dp2[i],dp0[i]+dp2[i]);
    }
}
int main ()
{
    init ();
    char str[25];
    int num[25];
    int cas ;
    scanf("%d",&cas);
    while (cas--)
    {
        scanf("%s",str);
        int len = strlen (str);
        for (int i=0 ; i<len ; ++i)
            num[i]=str[i]-48;
        ll cnt=num[0]*dp2[len-1];
        //printf("%lld\n",cnt);
        if(num[0]>4) cnt+=dp1[len-1];
        //printf("%lld\n",cnt);
        bool flag=0;
        for (int i=1 ; i<len ; ++i)
        {
            cnt+=num[i]*dp2[len-i-1];
            //printf("%lld\t",cnt);
            if(flag)cnt+=num[i]*dp0[len-i-1];
            if(!flag) if(num[i]>4) cnt+=dp1[len-i-1];
            //printf("%lld\n",cnt);
            if(!flag)if(num[i-1]==4 && num[i]==9)flag=1;
        }
        for (int i=0 ; i<len-1 ; ++i)
        if(num[i]==4 && num[i+1]==9)flag=1;
        if(flag)cnt++;
        printf("%I64d\n",cnt);
    }
    return 0;
}


 

你可能感兴趣的:(HDU 3555 Bomb (数位DP))