版权声明:写得不好,随便转载,但请注明出处,感激不尽 https://blog.csdn.net/xyc1719/article/details/83477788
【一句话题意】定义十进制下有3个连续的6的数为魔鬼数。有T个询问,求第k小的魔鬼数。
T<=1000,k<=5e7
【分析】由于K有5e7那么大,哪怕线性dp,常数稍大就会有TLE的风险。如果内存小于128MB又会有MLE的问题显然,预处理出第k大的魔鬼数是不可靠的。
由于T较小,我们转而考虑能否像计数dp一样将先大致预处理出辅助数组,再进行“拼凑”。回答是可行的。但dp数组的定义是与数字的位数有关。
定义f[i,k]表示i位数,开头有k个连续的6的数的数目。注意允许出现前导0
状态转移方程:
每次在询问时,利用辅助数组进行转移。
注意设计状态转移方程时,注意不重复不遗漏
【code】
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int f[200][4];
void Init(){
f[0][0]=1;
for(int i=0;i<=20;i++){
for(int j=0;j<3;j++){
f[i+1][j+1]+=f[i][j];
f[i+1][0]+=f[i][j]*9;
}
f[i+1][3]+=f[i][3]*10;
}
}
int main(){
int T,n,m;cin>>T;
Init();
while(T--){
scanf("%d",&n);m=3;
while(f[m][3]<n)m++;
for(int i=m,k=0;i>0;i--){
for(int j=0;j<=9;j++){
long long cnt=f[i-1][3];
if(j==6||k==3)
for(int l=max(3-k-(j==6),0);l<3;l++)
cnt+=f[i-1][l];
if(cnt<n) n-=cnt;
else{
if(k<3){
if(j==6) k++;else k=0;
}
printf("%d",j);
break;
}
}
}
cout<<endl;
}
return 0;
}