题目大意抽离出来就是有一个序列:1,3,9,27......其中第i个数等于3i-1。现在给你一个k,求出这个序列中的数能组成的数中,第k大的数。
来找一下规律:
首先先把i=4时的序列能组成的数排列出来:1,3,4,9,10,12,13,27,28,30,31,36,37,39,40。
再枚举k 1-6(所列出来的数是原数列中的,不是能组成的数的数列,也就是不是上方这个)(括号中的数表示不取):
k=1 ans=1
k=2 ans=(1+)3=3
k=3 ans=1+3=4
k=4 ans=(1+3+)9=9
k=5 ans=1+(3+)9=10
k=6 ans=(1+)3+9=12
我们用第i个1表示取第i个数,用第i个0表示不取第i个数。
那么就变成了这样:
1 3 9
k=1 ans=1
k=2 ans=0 1
k=3 ans=1 1
k=4 ans=0 0 1
k=5 ans=1 0 1
k=6 ans=0 1 1
我们发现如果把ans倒过来,并且把它当成二进制数,再把它变成十进制数,就是k!!!
如:ans=1 0 1 变为十进制就是 0×20+1×21+0×22+1×23=5=k
ans=1 1 0 变为十进制就是 0×20+1×21+1×22+0×23=6=k
所以我们不妨这样想:
先定义一个数组t,t[i]=3i-1,可以打表打出来,也可以用快速幂在程序中计算,但要i枚举到多少呢?
我们看k的取值范为,k<=231-1,所以kmax=231-1=2147483647。
2147483647转换成二进制就是1111111111111111111111111111111,有31位,说明t数组中最多有31个数,所以i枚举到31就行,可以打表,也可以用快速幂在程序中计算,都不会超时。
定义一个h储存答案。
要求第k大的数,就要把k转换为二进制数,再翻转过来,如果第i位上的数为1,那么h+=t[i],可以边读边处理。
十进制转换成二进制的代码:
1 int a[1000];
2 int o=1,k;
3 scanf("%d",&k);
4 while(k!=0)
5 {
6 a[o]=k%2;
7 k/=2;
8 o++;
9 }
10 for(int i=o-1;i>=1;i--)
11 printf("%d",a[i]);
这个需要倒叙输出,看到这,就联想到了前面的需要翻转,那我们为什么不直接不翻转来直接对应呢?想到这就可以更简化了,下面上代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n;
long long k,h;
long long t[35];
void dabiao();
int main()
{
freopen("recruitment.in","r",stdin);//如果题目没要求可以注释掉
freopen("recruitment.out","w",stdout);
dabiao();
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int o=1;//记录现在对应的是第几位数
h=0;
scanf("%lld",&k);
while(k!=0)
{
long long p;
p=k%2;
if(p==1) h+=t[o];
k/=2;
o++;
}
printf("%lld
",h);
}
return 0;
}
void dabiao()//打表
{
t[1]=1;
t[2]=3;
t[3]=9;
t[4]=27;
t[5]=81;
t[6]=243;
t[7]=729;
t[8]=2187;
t[9]=6561;
t[10]=19683;
t[11]=59049;
t[12]=177147;
t[13]=531441;
t[14]=1594323;
t[15]=4782969;
t[16]=14348907;
t[17]=43046721;
t[18]=129140163;
t[19]=387420489;
t[20]=1162261467;
t[21]=3486784401;
t[22]=10460353203;
t[23]=31381059609;
t[24]=94143178827;
t[25]=282429536481;
t[26]=847288609443;
t[27]=2541865828329;
t[28]=7625597484987;
t[29]=22876792454961;
t[30]=68630377364883;
t[31]=205891132094649;
}
by:ルオ?テンイの锦依卫
未经作者允许,禁止转载!