Subset sequence
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 7283 Accepted Submission(s): 3354
首先经过递推可以得出:n=1-->共有1*1个子集
n=2-->共有2*2个子集
n=1-->共有5*3个子集
n=1-->共有16*4个子集
n=1-->共有65*5个子集
所以当n为m时 n=m-->共有num(n - 1) *n个子集,num(n-1)表示有n-1个数时的子集个数
知道了这个以后我们要从第一个数字依次求解到最后一个数,每此求法都是相同的:
.设m为叫我们求的第m个子集, 令xu = m / (num(n) / n),如果不能整除就xu++,所以当前1到n中没有被选出的第xu小的数就是我们要找的数,把它输出,注意格式,经过分析你会发现,虽然少了一个数,但它的排列规律还是一样的,就是按照字典序来的,我们进行缩圈操作,既m = m - (xu - 1) * num(n); n--, m--(这是n--到来的后果,就是把前面的数字删掉,因为已经找到了),
反复执行上面的操作,直到m为零
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int inf = 21;
long long ans[inf];
bool fou[inf];
long long Find(long long xu)
{
int num = 0;
for(int i = 1; i <= 20; i ++){
if(fou[i] == false) num ++;
if(num == xu) return i;
}
}
int main()
{
ans[1] = 1;
for(int i = 2; i <= 20; i ++){
ans[i] = i * (ans[i - 1] + 1);
}
long long n, m;
while(scanf("%lld %lld", &n, &m) != EOF){
//表示开始一个都没有选中
memset(fou, false, sizeof(fou));
int N = 0;
while(m){
if(N == 1) cout << " ";
//表示每一个子集合有mei个
long long mei = ans[n] / n;
long long xu = m % mei == 0 ? m / mei : m / mei + 1;
m = m - (xu - 1) * mei;
n --;
m --;
xu = Find(xu);
fou[xu] = true;
cout << xu;
N = 1;
}
cout << endl;
}
return 0;
}