二进制集合操作
一个数的二进制表示可以看作是一个集合(00 表示不在集合中,11
表示在集合中).比如集合 {1,3,4,8}{1,3,4,8}
,可以表示成 (100011010)2(100011010)2
.而对应的位运算也就可以看作是对集合进行的操作.
| 操作 | 集合表示 | 位运算表示 |
|---|---|---|
| 交集 | 𝑎 ∩𝑏a∩b | 𝑎AND𝑏aANDb |
| 并集 | 𝑎 ∪𝑏a∪b | 𝑎OR𝑏aORb |
| 补集 | ¯𝑎a¯ | NOT𝑎NOTa |
| 差集 | 𝑎 ∖𝑏a∖b | 𝑎ANDNOT𝑏aANDNOTb |
| 对称差 | 𝑎△𝑏a△b | 𝑎XOR𝑏aXORb |
在进一步介绍集合的子集遍历操作之前,先看位运算的有关应用例子.
模 2 的幂
一个数对 22 的非负整数次幂取模,等价于取二进制下一个数的后若干位,等价于和 𝑚𝑜𝑑 −1mod−1
进行与操作.
C++Python
---|---
---|---
于是可以知道,22 的非负整数次幂对它本身取模,结果为 00
,即如果 𝑛n
是 22
的非负整数次幂,𝑛n
和 𝑛 −1n−1
的与操作结果为 00
.
事实上,对于一个正整数 𝑛n,𝑛 −1n−1
会将 𝑛n
的最低 11
位置零,并将后续位数全部置 11
.因此,𝑛n
和 𝑛 −1n−1
的与操作等价于删掉 𝑛n
的最低 11
位.
借此可以判断一个数是不是 22 的非负整数次幂.当且仅当 𝑛n
的二进制表示只有一个 11
时,𝑛n
为 22
的非负整数次幂.
C++Python
---|---
---|---
子集遍历
遍历一个二进制数表示的集合的全部子集,等价于枚举二进制数对应掩码的所有子掩码.
掩码是一串二进制码,用于和源码进行与运算,得到屏蔽源码的若干输入位后的新操作数.
掩码对于源码可以起到遮罩的作用,掩码中的 11 位意味着源码的相应位得到保留,掩码中的 00
位意味着源码的相应位进行置 00
操作.将掩码的若干 11
位改为 00
位可以得到掩码的子掩码,掩码本身也是自己的子掩码.
给定一个掩码 𝑚m,希望有效迭代 𝑚m
的所有子掩码 𝑠s
,可以考虑基于位运算技巧的实现.
---|---
或者使用更紧凑的 for 语句:
---|---
这两段代码都不会处理等于 00 的子掩码,要想处理等于 00
的子掩码可以使用其他办法,例如:
---|---
接下来证明,上面的代码访问了所有 𝑚m 的子掩码,没有重复,并且按降序排列.
假设有一个当前位掩码 𝑠s,并且想继续访问下一个位掩码.在掩码 𝑠s 中减去 11,等价于删除掩码 𝑠s 中最右边的设置位,并将其右边的所有位变为 11.
为了使 𝑠 −1s−1 变为新的子掩码,需要删除掩码 𝑚m 中未包含的所有额外的 11 位,可以使用位运算 `(s - 1) & m` 来进行此移除.
这两步操作等价于切割掩码 𝑠 −1s−1,以确定算术上可以取到的最大值,即按降序排列的 𝑠s 之后的下一个子掩码.
因此,该算法按降序生成该掩码的所有子掩码,每次迭代仅执行两个操作.
特殊情况是 𝑠 =0s=0.在执行 𝑠 −1s−1 之后得到 −1−1,其中所有位都为 11.在 `(s - 1) & m` 操作之后将得到新的 𝑠s 等于 𝑚m.因此,如果循环不以 𝑠 =0s=0 结束,算法的循环将无法终止.
使用 popcount(𝑚)popcount(m) 表示 𝑚m 二进制中 11 的个数,用这种方法可以在 𝑂(2popcount(𝑚))O(2popcount(m)) 的时间复杂度内遍历集合 𝑚m 的子集.
### 遍历所有掩码的子掩码
在使用状压 DP 的问题中,有时会希望对于每个掩码,遍历掩码的所有子掩码:
---|---
这样做可以遍历大小为 𝑛n 的集合的每个子集的子集.
接下来证明,该操作的时间复杂度为 𝑂(3𝑛)O(3n),𝑛n
为掩码总共的位数,即集合中元素的总数.
考虑第 𝑖i 位,即集合中第 𝑖i
个元素,有三种情况:
- 在掩码 𝑚m
中为 00
,因此在子掩码 𝑠s
中为 00
,即元素不在大小子集中.
- 在 𝑚m
中为 11
,但在 𝑠s
中为 00
,即元素只在大子集中,不在小子集中.
- 在 𝑚m
和 𝑠s
中均为 11
,即元素同时在大小子集中.
总共有 𝑛n 位,因此有 3𝑛3n
个不同的组合.
还有一种证明方法是:
如果掩码 𝑚m 具有 𝑘k
个 11
,那么它有 2𝑘2k
个子掩码.对于给定的 𝑘k
,对应有 (𝑛𝑘)(nk)
个掩码 𝑚m
,那么所有掩码的总数为:
𝑛∑𝑘=0(𝑛𝑘)2𝑘∑k=0n(nk)2k
上面的和等于使用二项式定理对 (1 +2)𝑛(1+2)n 的展开,因此有 3𝑛3n
个不同的组合.
参考资料
本页面主要译自博文Перебор всех подмасок данной маски 与其英文翻译版 Submask Enumeration.其中俄文版版权协议为 Public Domain + Leave a Link;英文版版权协议为 CC-BY-SA 4.0.
习题
- Atcoder - Close Group
- Codeforces - Nuclear Fusion
- Codeforces - Sandy and Nuts
- UVa 1439 - Exclusive Access 2
- UVa 11825 - Hackers' Crackdown
本页面最近更新: 2026/1/27 12:26:08,更新历史 发现错误?想一起完善?在 GitHub 上编辑此页! 本页面贡献者:Tiphereth-A, aofall, arielherself, c-forrest, gavinliu266, Great-designer, hhc0001, jol888, Menci, shawlleyw, TOMWT-qwq, ZnPdCo 本页面的全部内容在CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用