集合枚举子集-学习笔记
作者:互联网
集合枚举子集-学习笔记
有一个集合,请输出它的所有子集。
子集,即为被这个这个集合包括的所有集合,包括空集。那么显然,假如有 \(n\) 个元素,那么有 \(2^n\) 个子集。如何枚举子集呢?
首先有一个显然的方法:用 \(2^n\) 的 dfs 枚举。但这样有一个弊端:时空较大,而且比较麻烦。比如一个动态规划,你每次转移都要跑一遍 dfs ,时空开销很大,而且处理不方便。那么就有另外一种方法出现:循环枚举。
首先放代码:
for(int j=st;j;j=(j-1)&st) j2=st^j;
st
就是要枚举的集合,j
就是子集,j2
就是 j
相对于 st
的补集。这里我们认为集合是一个二进制数中所有元素 \(1\) ,一个 \(1\) 代表一个元素。为什么这样可以呢?
模拟一遍过程:st
=1101
,我们按照循环顺序模拟过程。
\(【1】1101\)
\(【2】1100\)
\(【3】1001\)
\(【4】1000\)
\(【5】0101\)
\(【6】0100\)
\(【7】0001\)
我们假定 \(0000\) 也在元素中,然后按照一定规律排列子集:
\[【1】1101\;\;\; 【5】0101 \]\[【2】1100\;\;\; 【6】0100 \]\[【3】1001\;\;\; 【7】0001 \]\[【4】1000\;\;\; 【8】0000 \]有没有发现规律?每一行,后三位都是相同的。所以这个算法的本质算是,对于每一个 \(1\) ,先把这一位设定为 \(1\) 枚举后面所有位,再把这一位设定为 \(0\) 枚举一遍,然后再往前面递归。因为减 \(1\) 可以看做把最后一个 \(1\) 设定为 \(0\) ,后面所有设定为 \(1\)。而且这个做法是从大到小枚举子集,着实十分优美。但要注意的是这种办法没法枚举空集,需要手动计算。
枚举子集在一些位运算的题目中可能会涉及,而这种方法可以很好地减少代码复杂度以及时空复杂度,同时也是一种很好的思想。
标签:设定,笔记,st,枚举,子集,1101,集合 来源: https://www.cnblogs.com/One-coder/p/16113670.html