0
点赞
收藏
分享

微信扫一扫

HDU 2062——Subset sequence(最详细的解释,看不懂你找我)

原题链接:​​http://acm.hdu.edu.cn/showproblem.php?pid=2062

Subset sequence

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 11832 Accepted Submission(s): 5013

Problem Description
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

题目大意:给定一个集合An= { 1, 2, …, n };按词典顺序对此集合的子集进行排列,找出第m个子集并输出。

解题思路:既然是按照词典顺序排列,那么即有规律可循,我们先来看A3的子集排列

{1}
{1, 2}
{1, 2, 3}
{1, 3}
{1, 3, 2}

{2}
{2, 1}
{2, 1, 3}
{2, 3}
{2, 3, 1}

{3}
{3, 1}
{3, 1, 2}
{3, 2}
{3, 2, 1}

我们发现,去除首元素,剩下的就是A2的排列规则和空集。那么对于An来说也是如此,去除首元素来说,剩下的就是An-1的排列规则和n个空集。
则f(n)=n*[f(n-1)+1],这明显就是一个迭代的过程。
又我们可以根据首元素来进行分组,则g(n)就为An中每一个分组的子集序列数,首元素为n。那么不然发现f(n)/n=g(n);
结合上述推导出来的两式。
我们可以进行如下推导发现f(n)和f(n-1)的关系,因为我们最重要的就是要知道f(n)的值。
f(n-1)=(n-1)g(n-1);
n
g(n)=n*[(n-1)g(n-1)+1]
则g(n)=(n-1)*g(n-1)+1
由此我们即可以递推出g(n)的值。

求出g(n)之后有什么用呢?我们且看一个例子。还是A3。
若m=10我们需要求出第10个子集序列并输出。那么我们通过分组可知每组有g(3)个子序列。又g(1)=1,则g(2)=2,g(3)=5。那么易知第10个子集序列在第二个分组,我们先取出2(已经确定首元素是2)。在第二个分组中我们要找其中第5个子集序列,这就是我们要的答案,对于第二个分组,我们可以剔除2,留下的就是A2的排列规则,那么我们即可以对A2(实际不是A2,只是都含两个元素遵循词典排序的序列)再进行分组,即每组有两个元素,当然,我们剔除2后还有一个空集,我们也要算进去,但按照词典排列规则可知,这个序列永远是第一个,我们可以直接用5-1=4!=0来计算,那么可知4-1/g(2)+1在第二个分组,那么此时m=m-(2-1)*g(2)=2,则依次进行此步骤。所以,此题的关键就是g(n),我们已经推导出公式,则此题易解。

下面是AC代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<algorithm>
#include<cmath>
#include<string>

using namespace std;

const int maxn=25;
int n;//n:An
long long m;// m:第m个子集序列。
long long g[maxn]={0}; //代表的就是g(n),即第n个分组中每组的子集数。
int num[maxn]; //存放分组后的首元素。
long long i;//循环控制变量
long long temp;//临时变量,代表所求m位于的分组

int main()
{
for(i=1;i<=maxn;i++)
g[i]=(i-1)*g[i-1]+1; //打g(n)表,供下面使用
while(cin>>n>>m)
{
for(i=0;i<=maxn;i++)
num[i]=i;
while(n>0&&m>0)
{
temp=(m-1)/g[n]+1; //确定在第几个分组
if(temp>0)//说明所求第m个子集位于第temp个分组。
{
cout<<num[temp];
for(i=temp;i<=n;i++)
{
num[i]=num[i+1];//我们将其首元素从中剔除,继续分组寻找下一个。
}
m=m-((temp-1)*g[n]+1);//要更新m的值,因为我们已经剔除了前面(temp-1)个分组,加1是因为序号是从1开始。
if(m!=0)cout<<" ";
else cout<<endl;
}
n--; //剔除元素,接下来就是在An-1中寻找第m个子集序列。
}
}
system("pause");
return 0;
}


举报

相关推荐

0 条评论