hdu-4734-数位Dp

按照题意推dp就好

从最高位开始,根据是否受限往下递推记忆化保存结果


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
typedef int  ll;
const int maxn=5005+500;
int ans=0;
int dp[11][maxn];
int aa[11];
//    pos    = 当前处理的位置(一般从高位到低位)
  //    sum =限制不超过 F(a)
 //    limit  = 是否受限,也即当前处理这位能否随便取值。如567,当前处理6这位,
 //            如果前面取的是4,则当前这位可以取0-9。如果前面取的5,那么当前
 //            这位就不能随便取,不然会超出这个数的范围,所以如果前面取5的
 //            话此时的limit=1,也就是说当前只可以取0-6。
 //
int F(int b)
{
      int len=0;
    while(b)
    {
        aa[++len]=b%10;
        b/=10;
    }
    int sum=0;
    for (int i=len; i>=1; i--)
    {
        sum+=(1<<(i-1))*aa[i];
    }
    return sum;
}
//    用DP数组保存这2个状态是因为往后转移的时候会遇到很多重复的情况。
//可根据需要增加维数,例如pre
int  dfs(int pos,int sum,int flag)
{
    int ans=0;
    if (pos==0)    //已结搜到尽头,返回"是否找到了答案"这个状态。
    {
        if (sum>=0) return 1;
        else return 0;
    }
    if (sum<0) return  0;
	  //DP里保存的是完整的,也即不受限的答案,所以如果满足的话,可以直接返回。
    if (!flag &&dp[pos][sum]!=-1) return dp[pos][sum];
    int up;
    if (flag) up=aa[pos];
    else up=9;
	//根据是否受限确定枚举的上界
    for (int i=0; i<=up; i++)
    {
        int ff;
        if (!flag) ff=0;
        else
        {
            if (i==up) ff=1;
            else ff=0;
        }
        ans +=dfs(pos-1,sum-i*(1<<(pos-1)),ff);
    }
	    //DP里保存完整的、取到尽头的数据
    if (!flag)
        dp[pos][sum]=ans;
    return ans;

}
int main()
{
    memset(dp,-1,sizeof dp);

    int tt,cas=0;
    scanf("%d",&tt);
    int m;
    int cnt=1;
    while (tt--)
    {
        int a,b;
        scanf("%d%d",&a,&b);

        int sum =F(a);
        int len=0;
        while(b)
        {
            aa[++len]=b%10;
            b/=10;
        }
        ans=dfs(len,sum,1);
        printf("Case #%d: %d\n",cnt++,ans);

    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值