问题来源:Problem - 2062
问题描述
问题解读:
这道题的大概意思是:有一个序列,比如,。要求输入一个n和一个m,n表示序列的长度,m表示第m个子集
问题解析:
当n=1时,子集只有;
当n=2时,子集有
当n=3时,子集有
根据上面的可以看出,按照首个元素子相同的子集进行分组,设F(n)为n的每一组子集个数,则,
接着,算出序号m位于第a组,输出第a组的首元素xa,这样就输出了子集的第一个元素,在原来An数组删除已经xa,m的位置就缩小到第a组内,变成了(因为该组首元素已经从数组中删去,所以后面要再减一),如果m不等于0,说明后面还有数字,就将第n组分为(n-1)组,然后重复上面的操作。
拿3 10来举例说明:
将A3分为3个组,一个组5个子集,可得10是在第2组,输出第2组的首个子集,在A3中删去2,只剩下,m就变成了,说明m是位于第二组删除了后第四个位置,因为m不等于0,说明后面还有数字,所以将第2组分为2个小组,4位于新的第二组,现在第二个元素是3,故输出,A3中删去3,只剩下,m就变成了,说明m是位于新的第二组删除了后第1个位置,因为m不等于0,说明后面还有数字,所以将新的第二组分为2个小组,1位于新的第一组,现在第一个元素是1,故输出,最后就输出了2,3,1.
实现代码:
import java.util.Scanner;
public class Test_2062 {
public static void main (String args[]) {
Scanner in = new Scanner(System.in);
int i;
int t; //表示m分配在第几组
int s[] = new int[23]; //每一组的首元素
long c[] = new long[23]; //每一组的元素个数
while(in.hasNextInt()) {
int n = in.nextInt();
long m = in.nextLong();
c[1] = 1;
for(i=2;i<=n;i++) {
c[i] = (i-1) * c[i-1] + 1;
}
for(i=1;i<=n;i++)
s[i] = i;
while(n>0 && m>0) {
t= (int)(m/c[n] + ((m%c[n]>0)?1:0));
if(t > 0) {
System.out.printf("%d",s[t]);
for(i=t;i<=n;i++)
s[i] = s[i+1];
m -= ((t-1)*c[n]+1);
if(m == 0)
System.out.println();
else
System.out.print(" ");
}
n--;
}
}
}
}
;