hdu-3943[数位dp(记忆化搜索)+二分]

求出区间 (P,Q] 中找到第K个满足条件的数,条件是该数包含X个4和Y个7

学习大神的模板后做的之前用递推式感觉要考虑的东西比较多,记忆化搜索就相对简化编程复杂度了

链接:模板

数位dp是取得每个数它v,0~v之间满足 有(x个4,y个7)条件的数的个数,那么可以知道随着v增大,满足(x个4,y个7)条件的数的个数是增大的。

有了这个单调性的条件就可以用二分来查找第k个数的位置。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_SIZE 25
typedef long long ll;
ll dp[MAX_SIZE][MAX_SIZE][MAX_SIZE];
ll p,q,x,y;
ll num[MAX_SIZE];
void init()
{
	memset(dp,-1,sizeof(dp));
}
ll dfs(ll pos,ll i,ll j,bool lim)
{//记忆化搜索 pos表示位置 i表示4的个数 j表示7的个数 
//lim表示上一个搜索的数位是否是num[pos],即后面的数能否列举
	if(pos==-1) return i==0&&j==0;
	if(i<0||j<0) return 0;//无效搜索
	if(!lim && ~dp[pos][i][j]) return dp[pos][i][j];
	ll res=0;
	ll up=lim?num[pos]:9;//上界
	for(ll d=0;d<=up;++d)
	{//逐项搜索
		res+=dfs(pos-1,i-(d==4),j-(d==7),lim&&d==up);
	}
	return lim?res:dp[pos][i][j]=res;
}
ll find(ll val)
{
	ll i=0;
	while(val)
	{
		num[i++]=val%10;
		val/=10;
	}
	num[i]=0;
	return dfs(i,x,y,1);
}
void query_k_th(ll k)
{	
	ll l=find(p),r=find(q);
	if(k>r-l)
	{
		printf("Nya!\n");
		return;
	}
	//二分
	k+=l;
	ll lb=p+1,rb=q;
	while((rb-lb)>=1)
	{
		ll mid=(rb+lb)>>1;
		// printf("%lld\n",find(mid));
		if(find(mid)>=k)
			rb=mid;
		else lb=mid+1;
	}
	printf("%lld\n",lb);
}
int main()
{
	int t;
	ll k;
	scanf("%d",&t);
	int cnt=1;
	while(t--)
	{
		printf("Case #%d:\n",cnt++);
		scanf("%lld%lld%lld%lld",&p,&q,&x,&y);
		init();
		// printf("%lld %lld\n",find(p),find(q));
		int n;
		scanf("%d",&n);
		while(n--)
		{
			scanf("%lld",&k);
			query_k_th(k);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值