HDU 4722 Good Numbers 数位dp或找规律枚举 数位dp感悟

数位dp感悟:
有一类与数位有关的区间统计问题。这类问题往往具有比较浓厚的数学味道,无法暴力求解,需要在数位上进行递推等操作
方法与 基本思想:
可以从高到低枚举第一次<n对应位是哪一位
¨需要统计区间[l,r]的满足题意的数的个数,这往往可以转换成求[0,r]-[0,l)
¨对于求区间[0,n)有一个通用的方法。
¨对于一个小于n的数,肯定是从高位到低位出现某一位<n的那一位。
¨预处理f数组。
¨F[i,st] 代表位数为i(可能允许前导0。如00058也是个5位数),状态为st的方案数。这里st根据题目需要确定。
¨如i=4,f[i,st]也就是0000~9999的符合条件的数的个数(十进制)
¨决策第i位是多少(suchas 0~9)
¨F[i,st] = F[i,st] + f[i–1,st']

¨st'为相对应的状态


题解:

求区间[l,r]之间有多少个数,各位数和能被10整除。

转化为get(x),区间[0,x]之间有多少个数满足条件。

则答案即为get(r)-get(l-1),问题转化为数位dp。

将x的各位数字储存在bit[]中。

考虑区间[0~5132]

可以转化成[0~999] 、[1000~1999]、[2000~2999]、[3000~3999]、[4000~4999]、[5000~5099]、[5100~5109]、[5110~5119]、[5120~5129]、[5130~5132]

令f[i][j]表示前i位和模10得j的方案数。若i为最后一位,若j=0则f[i][j]=1,否则f[i][j]=0。

用记忆化搜索实现数位dp

dp(int p,int m,bool flag),flag=true 表示当前位p无论取多少都小于数x 。

则dp(i,m) += dp(i+1,(m+k)%10) 0=<k<= (9 or bit[i])

#include <cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define LL long long 
#define bug puts("***********")
#define INF 0x3f3f3f3f

using namespace std;

int bit[100];
LL dp[100][100];
LL DP(int p,int m,int flag)
{
    if(p==0) return (m==0);
    if(dp[p][m]!=-1&&flag) return dp[p][m];
    int k=flag?9:bit[p];
    LL sum=0;
    for(int i=0;i<=k;i++)
        sum+=DP(p-1,(i+m)%10,flag||i!=k);         //因为bit[]中的数是倒着存的
    if(flag) dp[p][m]=sum;
    return sum;
}
LL solve(LL n)
{
    memset(dp,-1,sizeof(dp));
    int num=0;
   
    if(n<0) return 0;               /// 存在n== -1 
    while(n)        
    {
        bit[++num]=n%10;            //bit[]将数倒着存进去了
        n/=10;
    }
    return DP(num,0,0);
}
int main()
{
    int t;
    LL n,m;
    while(~scanf("%d",&t))
    {
        for(int cas=1; cas<=t; cas++)
        {
             scanf("%lld%lld",&n,&m);
            printf("Case #%d: %lld\n",cas,solve(m)-solve(n-1));      ///注意 n-1
        }
    }
    return 0;
}

方法二:

暴力代码用来找规律

 发现: 0-10    1

    0-100  10

           0-1000   100

           0-990  99

    0-992  100

    0-997   100

  基本规律为 n/10 + (1或0)

加1的情况为:n/10*10 到 n  有满足条件的  比如:997: 99 + (990到997是否有满足条件的,如果有则加1)

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
__int64 ff(__int64 m)
{
    if(m<0)
    return 0;
    __int64 temp=m/10,ans;
    __int64 i;
    ans=temp;
    for(i=temp*10;i<=m;i++)
    {
       __int64 sum=0,t=i;
       while(t)
       {
           sum+=t%10;
           t/=10;
       }
      if(sum%10==0)
      ans++;
    }
    return ans;
}
int main()
{
    int tcase ,tt=1;
    __int64 a,b;
    scanf("%d",&tcase);
    while(tcase--)
    {
        scanf("%I64d%I64d",&a,&b);
        printf("Case #%d: %I64d\n",tt++,ff(b)-ff(a-1));
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值