题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3943
题意大意:
求区间内第 k k k个有且仅有 x x x个4和 y y y个7的数
思路:
很明显的数位 D P DP DP……
因为要求第 k k k个符合条件的数,继而想到二分查找答案
详情见代码。。。
完整代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N = 31;
ll t,p,q,x,y,n,tot,num[N],f[N][N][N];//一共有i位数,有j位4,k位7
ll dfs(ll pos, ll pre, ll pre2, bool flag) {//位置,已经有了pre个4,pre2个7,
if(!pos) return pre == x && pre2 == y;//如数位为0,退出,判断当前答案是否符合条件
if(pre > x || pre2 > y) return 0;//不符条件
if(f[pos][pre][pre2] != -1 && !flag) return f[pos][pre][pre2];//记忆化
ll end = flag ? num[pos] : 9;//确定范围
ll res = 0;
for(ll j = 0; j <= end; j ++) {
if(j == 4) res += dfs(pos - 1, pre + 1, pre2, flag && end == j);//找到4的情况
else if(j == 7) res += dfs(pos - 1, pre, pre2 + 1, flag && end == j);//7
else res += dfs(pos - 1, pre, pre2, flag && end == j);//没找到
}
if(!flag) f[pos][pre][pre2] = res;//记忆化
return res;
}
ll work(ll m) {
tot = 0;
while(m) num[++tot] = m % 10,m /= 10;//拆分数位
return dfs(tot,0,0,1);
}
ll solve(ll left,ll k) {
ll l = p + 1,r = q;//二分
while(l < r) {
ll mid = (l + r) / 2;
if(work(mid) >= k + left) r = mid;
else l = mid + 1;
}
return l;//返回结果
}
int main() {
scanf("%lld",&t);
for(ll i = 1; i <= t; i ++) {
scanf("%lld %lld %lld %lld %lld",&p,&q,&x,&y,&n);
memset(f,-1,sizeof(f));//初始化
printf("Case #%lld:\n",i);
ll ans1 = work(q) , ans2 = work(p), ans3;//先求出小于等于p的符合条件的数的个数和大于q的个数
ans3 = ans1 - ans2;//区间内的数
while(n--) {
ll h;
scanf("%lld",&h);
if(ans3 < h) printf("Nya!\n");//如果个数小于k,找不到第k大的数
else printf("%lld\n",solve(ans2,h));//否则二分查找
}
}
return 0;
}