二进制枚举!

     首先我们来看一个问题,给定一个长度为n的集合a,现在要你求a的所有子集(不包括空集)并打印出来。

这个问题很明显可以用搜索去做,确定一下枚举起点,子集长度不断向后搜索,但是使用搜索很麻烦,所以我们可以用二进制枚举

首先一个二进制数中只包含0,1,在二进制枚举中0代表不选当前元素,1代表选当前元素。

例如对于一个长度为5的集合{1,2,3,4,5},我们选择1,3,5这3个元素,如下图

a12345
二进制10101
是否选不选不选

那么当前选择的状态就可以用二进制10101来表示,也就是十进制中的21。由于对于一个长度为n的集合,它的子集个数就为2^n,我们用十进制来代表当前选择的状态,我们就可以从0到2^n - 1,依次枚举2^n个子集,2^n可以表示为(1<<n),所以代码如下:

for (int i = 0;i < (1<<n); i++) {
......
}

我们知道了枚举了每个子集的状态之后,那怎么把子集元素取出来呢?

例如上面我们在{1,2,3,4,5}中取了1,3,4,二进制为10101,我们枚举的是二进制的十进制形式,也就是21,我们可以把21转化为二进制后取出为1的元素,但这很麻烦。

一个很巧妙的方式就是利用位运算。

1<<0=1(0);

1<<1=2(10);

1<<2=4(100);

1<<3=8(1000);

1<<4=16(10000);

...

1<<7=128(10000000);

...

很容易看出,我们只需要(1<<j) & 21,就可以判断二进制下21的每一位是0还是1,为1时输出a[j](对<<和&不懂的可以提前去看我之前总结的位运算知识点这里),所以可以这样写:

for (int j = 0;j < n; j++) {
    if (i & (1 << j)) cout << a[j] << " ";
} 

那么这道题的完整代码就可以这样写了

#include <bits/stdc++.h>
using namespace std;
int main() {
	int n;
	cin >> n;
	int a[n + 1];
	for (int i = 0;i < n; i++) cin >> a[i];
    for (int i = 0;i < (1<<n) ;i++) { //从0~2^n-1个状态
    	for (int j = 0;j < n; j++) { //遍历二进制的每一位
    		if (i & (1<<j)) cout << a[j] << " "; //如果选了就输出a[j]
    	}
    	cout << endl;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值