对于集合N={1,2,…,n}的子集,定义一个称之为“小于”的关系: 设S1={X1,X2,…,Xi},(X1<X2<…<Xi),S2={Y1, Y2, …,Yj},(Y1<Y2<…<Yj),如果存在一个k,(0≤k≤min(i,j)),使得X1=Y1,…,Xk=Yk,且k=i或X(k+1)<Y(k+1),则称S1“小于”S2。 你的任务是,对于任意的n(n≤31)及k(k<2n),求出第k小的子集。 输入输出格式 输入格式: 输入文件仅一行,包含两个用空格隔开的自然数,n和k。 输出格式: 输出文件仅一行,使该子集的元素,由小到大排列。空集输出0。 输入输出样例 输入样例#1: 3 4 输出样例#1: 1 2 3
首先观察,可以得到如下:
当n=3时:
{}<
{1}<{1,2}<{1,2,3}<{1,3}<
{2}<{2,3}<
{3}
当n=4时:
{}<
{1}<{1,2}<{1,2,3}<{1,2,3,4}<{1,3}<{1,3,4}<{1,4}<
{2}<{2,3}<{2,3,4}<{2,4}<
{3}<{3,4}<
{4}
当n=5时:
{}<
{1}<{1,2}<{1,2,3}<{1,2,3,4}<{1,2,3,4,5}<{1,3}<{1,3,4}<{1,3,4,5}<{1,4}<{1,4,5}<{1,5}<
{2}<{2,3}<{2,3,4}<{2,3,4,5}<{2,4}<{2,4,5}<{2,5}<
{3}<{3,4}<{3,4,5}<{3,5}<
{4}<{4,5}<
{5}
这样列出就可发现规律了。
具体算法为:先推出第k小的一个自己的第一个数字是多少,从而确定第一个数字,
再推出第二个数字,从第一个数字加1一直计算累加集合个数,
直到的到不超过k的最大的那个数字,就是第二个数字,
这样一直递推,推倒最后一个。
注意:终止条件已推出n个数字或第i个数字为空。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #include<string> 7 #include<queue> 8 using namespace std; 9 int n,k,x[100]; 10 int main() 11 { 12 scanf("%d%d",&n,&k); 13 k--; 14 if(k==0) {printf("0");return 0;} 15 x[0]=1; 16 for(int i=1;i<=33;++i) x[i]=x[i-1]*2; 17 for(int i=1;k && i<=n;++i) 18 { 19 if(k>x[n-i]) k-=x[n-i]; 20 else{ 21 k--; 22 printf("%d ",i); 23 } 24 } 25 return 0; 26 } 27