【数位dp】HDU3555:Bomb(入门题目)

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=3555

题目:


输入t个数,如x,统计1~x含49的数字个数,注意x会很大,普通的暴力绝对超时!

 

解题思路:


数位dp模版题,记录每一位数的数组a[]从下标1开始存数,limit表示第pos位前所有位上的数是不是都达到了上限

两种方法:1.求0~x之间不含49的数字个数,x+1-solve()即是结果

                   2.直接求含49的数字个数

注意两种方法到达递归边界时返回值的区别!

第二种方法直接计算时的细节解析:(先看代码)

sum+=limit?n%z[pos-1]+1:z[pos-1];

 如求1~5498间含49的数字个数,z[0]=1,z[pos]=z[pos-1]*10:

当千位为0,百位为4,十位为9时,limit为true,pos=2

n%z[pos-1]+1=5498%z[0]+1=9;即存在的情况有5490~5498共9个符合条件的数


当千位为0,百位为4,十位为9时,limit=false,pos=2

z[pos-1]=z[0]=10;即存在0490~0499共10个符合条件的数


当千位为0,百位为1,十位为4,个位为9时,limit=false,pos=1

z[pos-1]=z[0]=1,即只有0149一个符合条件的数

ac代码:


1.先求不含49

#include <iostream>
#include <cmath>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <sstream>
#define maxn 1000005
#define inf 0x3fffffff
using namespace std;
typedef long long ll;
ll dp[20][3],a[20],z[30];
ll n,t;
ll dfs(ll pos,ll pre,int sta,bool limit)//求0~n中不包含49
{
    if(pos==0) return 1;//注意返回的是1
    if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
    ll sum=0;
    ll up=limit?a[pos]:9;
    for(ll i=0;i<=up;i++)
    {
        if(pre==4&&i==9) continue;
        sum+=dfs(pos-1,i,i==4,limit && i==a[pos]);
    }
    if(!limit) dp[pos][sta]=sum;
    return sum;
}
ll solve(ll x)
{
    ll i=0;
    while(x)
    {
        a[++i]=x%10;
        x/=10;
    }
    return  dfs(i,-1,0,true);
}
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n;
        memset(dp,-1,sizeof(dp));
        printf("%lld\n",n+1-solve(n));
    }
    return 0;
}

2.直接求含49

#include <iostream>
#include <cmath>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <sstream>
#define maxn 1000005
#define inf 0x3fffffff
using namespace std;
typedef long long ll;
ll dp[20][3],a[20],z[30];
ll n,t;
ll dfs(ll pos,ll pre,int sta,bool limit)
{
    if(pos==0) return 0;//注意返回0
    if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
    ll sum=0;
    ll up=limit?a[pos]:9;
    for(ll i=0;i<=up;i++)
    {
        if(pre==4&&i==9)
            sum+=limit?n%z[pos-1]+1:z[pos-1];//直接计算,具体细节在解题思路
        else sum+=dfs(pos-1,i,i==4,limit && i==a[pos]);
    }
    if(!limit) dp[pos][sta]=sum;
    return sum;
}
ll solve(ll x)
{
    ll i=0;
    while(x)
    {
        a[++i]=x%10;
        x/=10;
    }
    return  dfs(i,-1,0,true);
}
int main()
{
    z[0]=1;
    cin>>t;
    for(int i=1;i<=30;i++)
        z[i]=z[i-1]*10;
    while(t--)
    {
        cin>>n;
        memset(dp,-1,sizeof(dp));
        printf("%lld\n",solve(n));
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值