数位dp基础(类似模板) HDU 2089+3555

2089:求区间内不含62 和4的数字的个数

简单的数位dp,数位dp也就是搜索,按照位数来搜的,所以时间上应该是很快的,对于搜索,确定好当前所在长度(也就是搜到的当前数的长度),约束条件,还有是不是最后一个这个条件,

代码+解释

/* I believe xiaoxuzizhucan*/


#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#include<math.h>
#include <queue>
long long a[100];
long long dp[200][5];
//dfs(当前所在的位数,上一个数的i是不是等于6,是不是最后一位)
long long dfs(long long pos,long long have,long long flag)
{
    if(pos==0)//如果长度为0,也就是所搜到最后了,所以返回1,也就是这个数不含4和62,一位如果含有4和62,那就不会搜索到这一步,在下面的搜素过程中就continue了
    {

        return 1;

    }
    if(flag==0&&dp[pos][have]!=-1)//同理,剪枝头,我不是很懂。。其实删去应该也是对的吧,
    {
        return dp[pos][have];
    }
    long long sum=0;
    long long end=flag?a[pos]:9;//求该为所能搜索的最大值
    for(long long i=0; i<=end; i++)
    {
        if(i==4||have&&i==2)//如果当前为为4,或者上一个状态为6并且这一个状态为2,也就是出现4或者出现62,那么就不要你继续往下搜索了。
            continue;
        sum+=dfs(pos-1,i==6,flag&&i==end);//如果当前没有出现过4和62,然后就判断当前位是不是等于6,
    }
    if(flag==0)//入股不是最后一位,那么久赋值,方便上面剪枝使用
        dp[pos][have]=sum;
    return sum;

}
long long solve(long long n)
{

    long long len=1;
    while(n)
    {
        a[len++]=n%10;
        n/=10;
    }
    //printf("%d\n",len);
    return dfs(len-1,0,1);
}

int main()
{

    long long n,m;
    memset(dp,-1,sizeof(dp));
    while(scanf("%I64d%I64d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)
            break;
        printf("%I64d\n",solve(m)-solve(n-1));
    }

}

3555:

题意,让你找小于n的数中含有49的个数有多少。

同样也是很裸的数位dp,就不断地搜索就好了呗,想好状态变化就好了。

代码+解释

/* I believe xiaoxuzizhucan*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
#include<math.h>
long long a[150];
long long dp[150][5];
//dfs(当前数的长度,上一位是不是6,是不是最后一位)
long long dfs(long long pos,long long have ,long long flag)
{
    if(pos==0)//长度为0如果have==2,也就是出现过49就返回1,表述出现了一个
    {
        if(have==2)
            return 1;
        else return 0;
    }
    if(flag==0&&dp[pos][have]!=-1)//剪枝,也就是说如果当前位不是最后一位,并且被访问过,就直接返回
        return dp[pos][have];
    long long end=flag?a[pos]:9;//确定当前为最大值,
    long long sum=0;
    for(long long i=0; i<=end; i++)
    {
        long long temphave=have;
        if(have==1&&i!=4)//如果上一个是1,这一个不是1,那么就不可能有49
            temphave=0;
        if(have==0&&i==4)//如果上一个不可能是49,但是这一个是4,那么标记为1,也就是表示上一个状态为4
            temphave=1;
        if(have==1&&i==9)//如果上一个状态为4(have=1)并且这个状态为9,那么就出现49了,所以have==2代表49出现过
            temphave=2;
        sum+=dfs(pos-1,temphave,flag&&i==end);
    }
    if(flag==0)//若果不是在最后一位
        dp[pos][have]=sum;
    return sum;
}
long long suan(long long x)
{

    long long len=1;
    while(x)
    {
        a[len++]=x%10;
        x/=10;
    }

    return dfs(len-1,0,1);
    

}

int  main()
{
    long long test;
    memset(dp,-1,sizeof(dp));
    scanf("%I64d",&test);
    while(test--)
    {
        long long n;
        scanf("%I64d",&n);
        printf("%I64d\n",suan(n));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值