描述
Given a set, generate all of its nonvoid proper subsets in ascending order.
输入
Each test case starts with 2 <= N <= 9, which is the number of elements in a set. The next line will have N integers 1 <= ai <= 9, representing the value of each element in the set in ascending order. The input is terminated at the end of the file (EOF).
输出
For each test case, you must print all the nonvoid proper subsets of the input set in ascending order. Afer each test case you must print a blank line.
输入样例1
2
1 2
4
1 2 3 4
输出样例 1
1 2 1 2 3 4 12 13 14 23 24 34 123 124 134 234
题意:
给定n个升序数字,设,从n个数字中选取k个数字依次组成非递减的不重复组合数字(k从1开始到n-1)
思路:
这个题目题意比较好理解,问题关键在于如何将这n个数字按照我们的需要不重不漏的输出。仔细观察样例我们可以发现,有点像我们在高中所学习的排列组合中的组合,例如:n=4时,首先从[1,2,3,4]中挑选出一个即C(1,4)存在4种情况,然后从[1,2,3,4]中选出两个来即C(2,4)存在六种情况(当然这里肯定是满足升序的,如果不限制大小先后的话情况就是A(2,4)),依次类推就为C(k,n)。但是如何在代码中实现我们的这种需要呢?
在这里我的室友向我讲述了一种思路并跟我分享了网站
这里要注意的是,网站里的内容向我们提供了一种思路,大概就是通过使用两个大小为n的数组a,b,a数组保存输入的n个数字,b数组中的元素表示对应的a数组的序号中的元素是否被使用到,1表示被使用,0表示未被使用。但是不能生搬硬套,因为仔细使用发现这并不完全符合我们的题意。
列出几组样例之后发现当遇到例如10011(145)的下一个数字按理来说是01110(234),但是按照图中画线的意思确是01011(245),因此要做出如下改良:
替换图中画线语句:将比发生换位1高位的所有1挪到靠近换位1的低位,这样才能得到我们的01110(234)。
接下来就是我们的编写程序的工作,这里我自建了计算C(k,n)的函数,这样的话可以得到输出长度为k的次数
int bid(int x)
{
if(x==0||x==1)
return 1;
else
return x*bid(x-1);
}
int num(int a,int b)//a<=b
{
return bid(b)/(bid(a)*bid(b-a));
}
核心代码部分
注意!!!一定要从后向前找'10',我一开始习惯性从前向后寻找,后续才发现错误
for(int i=n-1;i>=0;i--)
{
if(b[i]==0&&b[i-1]==1)
{
swap(b[i],b[i-1]);//从后向前遍历找到10并交换
int cum=0;
for(int j=i+1;j<n;j++)
{
if(b[j]==1)
cum++;
}
for(int j=i+1;j<=i+cum;j++)//使高于交换1的1尽可能向低位靠近
b[j]=1;
for(int j=i+cum+1;j<n;j++)
b[j]=0;
for(int j=0;j<n;j++)//先打印打一次的
{
if(b[j]==1)
cout<<a[j];
}
cout<<endl;
break;
}
完整代码:
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
int bid(int x)
{
if(x==0||x==1)
return 1;
else
return x*bid(x-1);
}
int num(int a,int b)//a<=b
{
return bid(b)/(bid(a)*bid(b-a));
}
int main()
{
int n;
while(cin>>n)
{
int a[n]={0};
for(int i=0;i<n;i++)
cin>>a[i];
int cnt = 0;
while(++cnt < n)//初始状态为n中选1
{
int count = num(cnt,n);//统计从n中选cnt个的个数
int b[n]={0};
for(int i=0;i<cnt;i++)
b[i]=1;
for(int i=0;i<n;i++)//先打印打一次的
{
if(b[i]==1)
cout<<a[i];
}
cout<<endl;
while(count--)
{
for(int i=n-1;i>=0;i--)
{
if(b[i]==0&&b[i-1]==1)
{
swap(b[i],b[i-1]);//从后向前遍历找到10并交换
int cum=0;
for(int j=i+1;j<n;j++)
{
if(b[j]==1)
cum++;
}
for(int j=i+1;j<=i+cum;j++)//使高于交换1的1尽可能向低位靠近
b[j]=1;
for(int j=i+cum+1;j<n;j++)
b[j]=0;
for(int j=0;j<n;j++)//先打印打一次的
{
if(b[j]==1)
cout<<a[j];
}
cout<<endl;
break;
}
}
}
}
}
return 0;
}