ACM杂题I——Subset sequence题解
题目描述
Consider the aggregate An= { 1, 2, …, n }. For example, A1={1}, A3={1,2,3}. A subset sequence is defined as a array of a non-empty subset. Sort all the subset sequece of An in lexicography order. Your task is to find the m-th one.
Input
The input contains several test cases. Each test case consists of two numbers n and m ( 0< n<= 20, 0< m<= the total number of the subset sequence of An ).
Output
For each test case, you should output the m-th subset sequence of An in one line.
Sample Input
1 1 2 1 2 2 2 3 2 4 3 10
Sample Output
1 1 1 2 2 2 1 2 3 1
题目大意
由1—n共n个数的组成的集合,求出其非0子集,并将所有子集按照字典序排列,打印出第m个。
题解
先观察下n个数组成的子集的排列规律
n=1 1 n=2 1 n=3 1
1 2 1 2
2 1 2 3
2 1 1 3
1 3 2
2……
3…… 观察可以发现子集个数的递归关系为
,
An表示第n个集合,an表示第n个集合的非0子集个数
所以可以看出An是可以分为n组,每组的第一位分别是1~n,因此第m个数组的首位可以通过m/an-1来确定,现在相当于在大组中找到了m属于哪一个小组;接下来在这个小组内部,可以发现除去第一位,他的其余位也是按照从小到大的子集字典序排列的,因此可以发现,可以将m/an-1的余数m%an-1,减去1再除以an-2,求出“第二位”,而这个第二位不单纯就是(m%an-1)/an-2,而是删除第一位后An剩余几位从小到大排序的第m%an-1/an-2位,从而求出了m位于该小组的第几小小组,一直这样求下去直到((m%an-1)%an-2 -1)%……等于0,停止,说明求出了所有位。
n=1 1 n=2 1 n=3 1
1 2 1 2
2 1 2 3
2 1 1 3 第4行
1 3 2
2……
3……
减一的来源是这样的: 在大组中是第m行,在小组中因为去掉了高位的那一行,所以在小组中为第 (m%an-1) -1行
举一个栗子说明
n=3 m=14
An={1,2,3}
将子集个数打表方便以后每一次可以使用,减少时间复杂度,从c[1]~c[n]=1,4,15,64……
第一位 14/5=2···4, 为An[2]=3; 在An中将3删去, An={1,2};
第二位 4-1/2=1···1,为An[1]=2; 在An中将2删去,An={1};
算第三位时 1-1/1=0,停止计算
因此输出为 3 2。
代码
#include<iostream>
#include<stdio.h>
#include<vector>
using namespace std;
int main()
{
long long int m=0,n=0;
long long int a[21]={0};
a[0]=2;
a[1]=1;
for(long long int i=2;i<21;i++)
a[i]=i*(a[i-1]+1); //递归打表a
while(scanf("%lld%lld",&n,&m)!=EOF)
{
long long int *b=new long long int[n];
vector<long long int>c;
for(int i=1;i<=n;i++)
{
c.push_back(i); //输入集合C
b[i-1]=0; //初始化数组b,用于存放要打印的结果
}
long long int nm=m,nc=m;
for(long long int i=1;(i<=n && nc!=0);i++)
{
long long int index=0; //存放算出的是集合C中的第几位
if(nm%(a[n-i]+1)==0)
index=nm/(a[n-i]+1)-1; //整除的时候需要后移一位
else
index=nm/(a[n-i]+1); //除以n-1的个数加1为每一小组的个数
b[i-1]=c[index];
c.erase(c.begin()+index); //集合更新
nc=((nm-1)%(a[n-i]+1));
nm=nc;
}
for(int i=0;i<n && b[i]!=0;i++) //打印结果
{
if(i==n-1 || b[i+1]==0)
cout<<b[i];
else
cout<<b[i]<<" ";
}
cout<<endl;
}
return 0;
}