ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

关于状压DP枚举子集的方法与理解

2022-05-23 20:02:08  阅读:151  来源: 互联网

标签:S1 状压 枚举 子集 bigoplus 集合 DP Rightarrow


我们现在要枚举状压集合 \(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

标签:S1,状压,枚举,子集,bigoplus,集合,DP,Rightarrow
来源: https://www.cnblogs.com/houhaibushihai/p/16302592.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有