HDU 3555 Bomb 数位DP

一:题意 
给定一个 N,要求你求 出[0,N]内所有含有49的数字个数,其中N (1 <= N <= 2^63-1)。 
二:解析 
1,对于这一类数位dp,需要扫描[0,N]区间所有数的每一位,为了避免对每一个数取出每位数。我们利用数组来存储区间[0,N]的每一个数,数组的长度为N的位数 x (即最大位数) ,对于没有x位的数来说,我们在前位数面补0即可,这样我们只要扫描该最长数组,枚举数组每一位可能取的数(0,1,,,,9),就可以扫描区间[0,N]所有数字。 
2,dp数组含义 
dp[len][0] 表示前驱为非4,长度为len里面 “49” 数的个数。 
dp[len][1] 表示前驱为4,长度为len里面 “49” 数的个数。 
注意: 

这些记录都是在没最大数限制的情况下记录的,所以在调用时必须不受大数限制(即前驱不是最大值)。这是因为我们每次输入的最大值是不同的,所以受最大值限制的数也会不同(即没有通用性),只要没受最大数限制的那些数对所有的N都可用(即具有通用性)。


#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
typedef long long LL;
LL dp[30][2];
//dp[len][0] 长度为len,且前驱不是4的个数
LL total[30];
int bit[30];
LL T,N;

LL DFS(int Len,bool four,bool Max)
{//长度,前驱是不是4, 前面是不是最大值
    if(Len==0)
         return 0;
    if(!Max&&(dp[Len][four]!=-1))
         return dp[Len][four];//不能是最大值时不受限制
    LL res=0;
    int bian=9;
    if(Max)//len层循环的边界
        bian=bit[Len];
    for(int i=0;i<=bian ;i++)
    {//从0开始所以可以遍历所有0——N数
        if(four&&i==9)
        {//出现49就剪枝
            if(Max)//前驱全是最大值的情况
                res+=(N%total[Len-1])+1;
            else
                res+=total[Len-1];
        }
        else
            res+=DFS(Len-1,i==4,Max&&(i==bit[Len]));
    }
    if(!Max)//记录非最大值下一个N再用
        dp[Len][four]=res;
    return res;
}

void solv(LL M)
{
    int len=1;
    while(M)
    {
        bit[len++]=M%10;
        M=M/10;
    }
    bit[len]=0;//所以[0,N]的数第len为都为0
    cout<<DFS(len-1,false,true)<<endl;
}

int main()
{
    total[0]=1;
    for(int i=1;i<30;i++)
        total[i]=total[i-1]*10;
    memset(dp,-1,sizeof(dp));
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld",&N);
        solv(N);
    }
    return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值