F(x) HDU - 4734+数位dp+求剩余而非直接求和

本题题目链接
数位dp知识点例题总结

题目大意:
给定a,b;求出f(a);判断[0,b]内,f(x)小于等于f(a)的数目
定位:数位dp;
比较:dp[pos][sum]
第一种:pos表示当前的位置,sum表示到了当前位置的数;
dp[pos][sum]表示pos位置,sum数据,达到满足要求的数的个数。pos==-1时,return sum>=fa(a);
第二种:pos当前位置 ,sum表示:  f(a)-- 之前位置的值剩下的值;
dp[pos][sum]表示pos位置,用sum数据减去后面的数,结果大于等于0 的数的个数.pos==-1时,return sum >=0;

分析:
第一种:
题目是多组输入输出,而第一次使用后:dp[pos][sum]表示的是:dp[pos][sum]小于等于 第一组f(a) 的数
以后使用显然是不对的;如果每次都初始化dp,显然会超时。
第二种:
初始时的f(a)是不同的;
而确定了当前剩余位置的值,而后面的值又是确定的;
对于每一组测试数据来说,结果都是唯一的,这个dp方程就是可以通用的。
心得:
对于多组输入的数据,我们在外面对dp方程初始化,从而达到使用之前处理过的数据
!可是要思考一个问题:我们所定义的dp方程是针对一个问题的还是针对多个问题的
!如果是针对一个问题的,那么这个dp方程就不能单次初始化,甚至不可使用。
代码中关键部位有解析。
// (0 <= A,B < 10 9)
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
int dp[10][37000];//每个数都是9,计算出来接近37000,
int a[12];
int id[12];//
int judge(int a)
{
	int t[12];
	int pos=0;
	while(a)
	{
		t[pos++]=a%10;
		a/=10;
	}
	int ans=0;
	for(int i=0;i<pos;i++)
	{
		ans+=t[i]*id[i];
	}
	return ans;
}
int dfs(int pos,int sum,bool limit,bool lead)
{
	if(sum<0) return 0;//如果在计算过程中,sum的值小于0,后面也不用判断了。
	if(pos==-1) return sum >= 0;
	if(!limit&&!lead&&dp[pos][sum]!=-1) return dp[pos][sum];
	
	int up=limit?a[pos]:9;
	int ans=0;
	for(int i=0;i<=up;i++)
	{
		ans+=dfs(pos-1,sum-i*id[pos],limit&&(i==up) ,lead&&(i==0));
	}
	
	if(!lead&&!limit)  dp[pos][sum]=ans;
	
	return ans;
}
int solve(int k,int u)
{	
	int mm=judge(u);
	int pos=0;
	memset(a,0,sizeof(a));
	while(k)
	{
		a[pos++]=k%10;
		k/=10;
	}
	int ans=dfs(pos-1,mm,1,1);
	return ans;
}
int main()
{
	id[0]=1;
	for(int i=1;i<=11;i++)
	{
		id[i]=id[i-1]*2;
	}
	
	int t,u,v;
	scanf("%d",&t);
	memset(dp,-1,sizeof(dp));

	for(int i=1;i<=t;i++)
	{
		scanf("%d%d",&u,&v);
		printf("Case #%d: %d\n",i,solve(v,u));
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值