C/C++ 子集生成算法整理

个人笔记,仅供复习

1.概念:给定一个集合,枚举所有可能的子集。

2.常用算法

  • 增量构造法
  • 位向量法
  • 二进制法

3.增量构造法

3.1 思路:一次选出一个元素放到集合中。

3.2 代码实例:

void print_subset(int n,int *A,int cur){
	for(int i = 0;i < cur;i++)	cout << A[i] << " ";//打印当前子集 
	cout << endl;
	int s = cur ? A[cur-1]+1:0;
	for(int i = s;i < n;i++){
		A[cur] = i;
		print_subset(n,A,cur+1);
	}
}

3.3代码解析:

  • 其中cur指的是当前元素的位置。上述代码的作用是读入n,输出{0,1,2,3,…,n-1}的所有子集。第五行中的 s 指的是 当前还可以放入集合A中的最小元素。
  • 上述代码用到了定序的技巧。所谓的定序就是规定集合A中的所有元素按照从小到大排列,这样就不会出现{1,2}和{2,1}这样重复的集合了。
  • 上述代码的作用是输出{0,1,2,3,…,n-1}的所有子集。如果要输出自定义集合的子集,只需要将A[i]当作下标,这样就可以输出自定义的集合了。

4.位向量法

4.1 思路:构造一个位向量B[i],而不是直接构造集合本身,其中B[i] = 1,当且仅当i在子集A中。

4.2 代码实例:

void print_subset(int n,int *B,int cur){
	if(cur == n){
		for(int i = 0;i < cur;i++)
			if(B[i])	cout << i << " ";
		cout << endl;
		return;
	}
	B[cur] = 1;
	print_subset(n,B,cur+1);
	B[cur] = 0;
	print_subset(n,B,cur+1);
}

5.二进制法

5.1 思路:可以用二进制表示子集,其中从右往左第i位(从0开始编号)表示元素i是否在集合中(1表示在,0表示不在)。

5.2 代码实例:

#include<iostream>
using namespace std;
void print_subset(int n,int s){
	for(int i = 0;i < n;i++)
		if(s&(1<<i))	cout << i << " ";
	cout << endl;
}
int main()
{
	int n;
	cin >> n;
	for(int i = 0;i < (1<<n);i++)
		print_subset(n,i);//枚举各子集对应的编码0,1,2,…,2^n-1.
	return 0;
}

5.3 代码解析:

  • 首先要明白位移运算符(<<)的作用;第五行对1使用位移运算符,(1<<i)指的是将1向左移动i位,右面补0。
  • 若一个集合中有n个元素,那么它的子集有2^n个,而假设每个元素只有存在(1)和不存在(0)两种状态,那么它的子集可以看成不同的二进制串,即每个子集可以代表且仅代表一个不同的十进制数,共2^n个不同的十进制数,这也就说明了每个十进制数代表了一个不同的二进制串,即每个十进制数代表了一个子集,因此可以有上述代码第13行写法。具体关系如下:
                                    十进制数——>二进制串——>从右往左读串,是1则当前位数存在于此集合

转载于:https://www.cnblogs.com/long98/p/10352238.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值