求出区间 (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;
}