您的位置:首页 > 其它

HDU 3555 Bomb 数位DP

2015-08-10 17:22 411 查看
一:题意

给定一个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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: