关于状压DP枚举子集的方法与理解
2021/8/30 6:06:42
本文主要是介绍关于状压DP枚举子集的方法与理解,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
我们现在要枚举状压集合 \(S\) 的子集,代码实现:
for (int S1=S;S1!=0;S1=(S1-1)&S) { S2=S^S1; }
其中 \(S_1\) 就是我们枚举得到的子集,\(S_2\) 是当前子集 \(S_1\) 在 \(S\) 内的补集,即 \(S_1 \bigoplus S_2 = S\)
\[{\because S_2 = S \bigoplus S_1} \]\[{\therefore S_2 \bigoplus S_1 = S \bigoplus S_1 \bigoplus S_1} \]\[{\therefore S_2 \bigoplus S_1 = S \bigoplus (S_1 \bigoplus S_1)} \]\[{\therefore S_2 \bigoplus S_1 = S \bigoplus 0} \]\[{\therefore S_2 \bigoplus S_1 = S} \]赘述如下
现在来讲一讲为什么是这样的一个枚举方法,先让我们来举一个例子来模拟一下。
假设我们当前要枚举的是 \((10110)_2\) 的子集(子集仍然用 \(S_1\) 表示):
\(S_1 = (10110)_2 \Rightarrow (10100)_2 \Rightarrow (10010)_2 \Rightarrow (10000)_2 \Rightarrow (110)_2 \Rightarrow (100)_2 \Rightarrow (10)_2\)
根据例子,我们发现按照上面代码得到的结果是正确的,并且是把子集按照从大到小的顺序枚举出来的。那么接下来我们来谈谈这样枚举的正确性。
首先,一个集合它自己本身也是自己的一个集合,所以我们从这个集合本身开始枚举。
既然是枚举,那我们就先考虑把当前枚举得到的子集先 \(-1\)(即 \(S_1 - 1\)),但是这样做不能保证 \(-1\) 后得到的状态是原状态的子集,但是我们注意到:根据与运算&的性质,我们不难发现如果两个数 a, b,a < b,我们对这两个数进行&运算,最后的结果一定是b的子集,因为我们与运算&得到的结果,其二进制中所有1出现的位置在b的二进制对应位置也是1。
现在已经说明了这样做确实得到了原集合的一个子集 \(S_1\),但是还没有说明我们通过上述方式枚举,可以枚举完原集合 \(S\) 的所有子集。
其实枚举子集就相当于在原集合的二进制状态下把一些1换为0,而我们每次 \(-1\) 然后进行与运算其实就是在把当前子集 \(S_1\) 的最右边的1的右边全部变为1,自己变为0,然后和集合 \(S\) 进行与运算把新增的1中不该出现的抹去,最后只剩下了原集合中存在的1了。
Preference
- 关于状压DP枚举子集的方法与理解 备份次贴
- 二进制状态压缩枚举子集 从另一个角度证明
- OI Wiki - 位运算
这篇关于关于状压DP枚举子集的方法与理解的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2025-01-12百万架构师第十五课:源码分析:Spring 源码分析:SpringMVC核心原理及源码分析|JavaGuide
- 2025-01-11有哪些好用的家政团队管理工具?
- 2025-01-11营销人必看的GTM五个指标
- 2025-01-11办公软件在直播电商前期筹划中的应用与推荐
- 2025-01-11提升组织效率:上级管理者如何优化跨部门任务分配
- 2025-01-11酒店精细化运营背后的协同工具支持
- 2025-01-11跨境电商选品全攻略:工具使用、市场数据与选品策略
- 2025-01-11数据驱动酒店管理:在线工具的核心价值解析
- 2025-01-11cursor试用出现:Too many free trial accounts used on this machine 的解决方法
- 2025-01-11百万架构师第十四课:源码分析:Spring 源码分析:深入分析IOC那些鲜为人知的细节|JavaGuide