生成子集问题 --- 位向量法,增量构造法,二进制法

生成子集问题 --- 位向量法,增量构造法,二进制法

问题描述

之前我们写过背包问题,知道背包问题是找到最有价值的子集,但是如果需要把所有的子集全部输出出来该怎么做呢?
即:给定一个集合,枚举所有的可能的子集。

位向量法

  • 思路:
    其实我觉得这更可以叫做直接选择法,在需要枚举的1-n中选择第i个选择是否要放入当前子集。
  • 代码演示:
#include <iostream>
using namespace std;
const int MAXN = 10000;
int n;//集合中的元素个数
int visit[MAXN];

int subset(int num)
{
	if (num == n + 1)//已经判断了n个元素是否选择后输出选择的元素
	{
		for (int i = 1; i <= num; i++)
		{
			if (visit[i])
				cout << i << " ";
		}
		cout << endl;
		return 0;
	}
	//选择第num个元素作为子集的一部分
	visit[num] = 1;
	subset(num + 1);
	//不选择第num个元素作为子集的一部分
	visit[num] = 0;
	subset(num + 1);
	return 0;
}

int main()
{
	memset(visit, 0, sizeof(visit));
	cout << "请输入集合中元素的个数:";
	cin >> n;
	cout << 0 << endl;//输出0即空集
	subset(1);
	return 0;
}

运行结果如下:

增量构造法

  • 思路:
    根据递增顺序构造子集,规定集合A中所有元素的编号从小到大排列, 就不会把集合{1, 2}按照{1, 2}和{2, 1}输出两次了。
  • 代码演示:
#include <iostream>
using namespace std;
const int MAXN = 10000;
int n;//集合中的元素个数
int arr[MAXN];//存储子集

int subset(int arr[], int num, int n)
{
	if (arr[0] == 0 && num != 1)//去掉arr[0]==0并且num!=1时的子集
	{							//因为在有元素的情况下我们不区分是否有空集,即任何非空集合都有空集
	}
	else
	{
		for (int i = 0; i < num; i++)//输出子集
		{
			cout << arr[i] << " ";
		}
		cout << endl;
	}
	//确定可以加入子集的元素中的最小值m,当子集个数num=0时最小值为0,当子集个数不为0时最小值等于arr[num-1]+1
	//即当前子集最后一个元素加1
	int m = num ? arr[num - 1] + 1 : 0;
	for (int i = m; i <= n; i++)//选择加入子集中的元素
	{
		arr[num] = i;//将i加入当前子集中
		subset(arr, num + 1, n);//递归构造新的子集
	}
	return 0;
}

int main()
{
	cout << "请输入集合中元素的个数:";
	cin >> n;
	subset(arr, 0, n);
	return 0;
}

运行结果如下:

二进制法

  • 思路:
    仔细了解生成子集的问题,我们发现其实只需要选择每一位是否选择(即是否添加进当前子集),选择与否,我们可以联想到0和1,由此可以联想到二进制,因为二进制每一位都是0或者1构成。那么我们只需要写出0-111…1(n个1)所有的情况,就是子集。从右往左数第i位为1即代表当前子集中右i。
    那么由此我们便可以直接遍历1-(1<<n)输出所有情况即可。
  • 代码演示:
#include <iostream>
using namespace std;
int n;

int subset(int m)
{
	for (int i = 0; i < n; i++)
	{
		if (m & 1<<i)//判断从右到左数第i位二进制是否为1
		{
			cout << i+1 << " ";
		}
	}
	cout << endl;
	return 0;
}

int main()
{
	cout << "请输入集合中元素的个数:";
	cin >> n;
	cout << 0 << endl;//输出0,表示空集
	for (int i = 1; i < (1 << n); i++)//1<<n表示n+1位2进制数
	{
		subset(i);
	}
}

运行结果如下:

猜你喜欢

转载自blog.csdn.net/qq_41879343/article/details/89636827