集合的子集生成(无重复元素)

1:增量构造法

#include <iostream>
#include <algorithm>
using namespace std;

#define N 100000
int p[N];
int a[N];

//子集生成
void print_subset(int n, int *p, int *a ,int cur,int index)
{
	if (cur != 0)
	{
		for (int i = 0; i < cur; i++)
			cout << a[i] << " ";
		cout << endl;
	}
	for (int j = index; j < n;j++)
	{
		a[cur] = p[j];
		print_subset(n, p, a, cur + 1,j+1);
	}
}

int main()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> p[i];
	print_subset(n, p, a, 0,0);
	return 0;
}
2:位向量法

#include <iostream>
#include <algorithm>
using namespace std;

#define N 100000
int p[N];
int a[N];

//子集生成
void print_subset(int n, int *p, int *a ,int cur)
{
	if (cur == n)
	{
		for (int i = 0; i < cur; i++)
			if(a[i])	cout << p[i] << " ";
		cout << endl;
		return;
	}
	a[cur] = 1;
	print_subset(n, p, a, cur + 1);
	a[cur] = 0;
	print_subset(n, p, a, cur + 1);
}

int main()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> p[i];
	print_subset(n, p, a, 0);
	return 0;
}

3:二进制法(最简洁的方法)

#include <iostream>
#include <algorithm>
using namespace std;

#define N 100000
int p[N];
int a[N];

//子集生成
void print_subset(int n, int *p)
{
	for (int i = 0; i < (1 << n); i++)\
	{
		for (int j = 0; j < n; j++)
			if (i&(1 << j)) cout << p[j] << " ";
		cout << endl;
	}
		
}

int main()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> p[i];
	print_subset(n,p);
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个非常经典的NP问题,称为0/1背包问题。它可以使用动态规划或回溯搜索的方法解决。 1. 动态规划 定义状态dp[i][j]表示前i个数(即w1~wi)中选取一些数,使它们的和等于j的方案数。状态转移方程为: dp[i][j] = dp[i-1][j] + dp[i-1][j-wi] (当j>=wi) 其中,dp[i-1][j]表示不选取wi,dp[i-1][j-wi]表示选取wi。最终的答案是dp[n][M]。这种方法的时间复杂度为O(nM)。 2. 回溯搜索 使用回溯搜索的方法,我们可以枚举每个数选或不选,从而生成所有可能的子集,再判断它们的和是否等于M。这种方法的时间复杂度为指数级别,即O(2^n)。 但是,我们可以采用一些剪枝的策略,使搜索空间尽量缩小,从而提高效率。一些可行的剪枝策略包括: - 在搜索时按照数列的顺序,对于某个数,如果它前面的数已经选出的和加上它本身已经大于M了,那么它后面所有的数都不必再考虑了,因为它们加起来也一定大于M; - 如果一个集合的和已经大于M了,那么在后续的搜索中也没有必要再往里面添加更多的数,因为它们只会让和更大,无法满足等于M的条件; - 去除重复子集,例如当w1=1,w2=2时,{1,2}和{2,1}是同一个子集,只需搜索一个即可。 采用这些剪枝策略后,回溯搜索的时间复杂度会有所降低,但仍然是指数级别的,因此对于大规模的输入,这种方法的效率并不高。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值