E.Generate nonvoid proper subsets

本文介绍了一种算法,用于生成给定集合的所有非空真子集,并按升序排列。通过对组合数学的理解和运用,文章详细解释了如何通过两个数组实现这一过程,包括寻找下一个组合的具体步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

描述

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个升序数字,设1\leqslant k\leq n-1,从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)。但是如何在代码中实现我们的这种需要呢?

在这里我的室友向我讲述了一种思路并跟我分享了网站

关于排列的C++代码实现

这里要注意的是,网站里的内容向我们提供了一种思路,大概就是通过使用两个大小为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;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值