请考虑一本书(https://cses.fi/book/book.pdf)中的以下问题: “我们首先考虑生成一组n个元素的所有子集的问题。例如,{0,1,2}的子集为φ,{0},{1},{2},{0,1} ,{0,2},{1,2}和{0,1,2}。有两种常见的生成子集的方法:我们可以执行递归搜索,也可以利用整数的位表示。”
本书中的解决方案编号2(第48页,PDF第58页)如下:
for (int b = 0; b < (1<<n); b++) {
vector<int> subset;
for (int i = 0; i < n; i++) {
if (b&(1<<i)) subset.push_back(i);
}
}
我的问题是:为什么(b&(1<<i))
比较有效?到底在后台做什么?我已经手动测试了{0,1}的子集,它可以完美地工作,但是为什么它们之间的比较起作用呢? b
是元素数量的计数器,(1<<i)
从我的理解上基本上等于2 i 。为什么一切正常?好像魔术。
PS。我知道这本书是C ++的不好参考,我会读更多的书,以便进行算法解释。
答案 0 :(得分:3)
为集合中的每个元素分配不同的2(1、2、4、8等)的幂作为ID。每个子集是集合中各种元素的组合,可以用ID的组合表示。在子集中添加ID会得到一个唯一的编号。
这也可以反过来进行:从0到2 n -1的每个数字都代表该集合的子集,其中0代表空集合(因为没有元素存在),而2代表 n -1是其中包含所有元素的子集。将数字从0递增到2 n -1将枚举所有可能的子集。
1 << i
对应于其中一个ID,b & (1 << i)
将检查当前子集以查看元素i
是否属于其中。
答案 1 :(得分:3)
在这里,b
中的每一位代表相应的元素是否在子集中。从0到2 n 的循环遍历n
位的所有可能状态,因为每个状态都是0到2 n 之间的整数。因此,b
的值将代表所有可能的子集。
为了将b
的值转换为相应的子集,我们在每个元素上循环并测试该元素是否在子集中。如果在b
中设置了相应的位,则将元素添加到向量中。要测试i
中的第b
位是否已设置,我们计算b & (1 << i)
。
&
是按位与的,因此,只有在两个操作数中都设置了该位的情况下,结果的一位才会被设置。 1 << i
是位掩码,第i
位被置位,所有其他位均未置位。当我们计算b
和1 << i
的按位与时,第i
位以外的所有其他位都不会被设置,因为在位掩码中未设置相应的位。仅当i
中的第i
位被置位时,第b
位将被置位。因此,如果第i
位被置1,我们将得到除0之外的值,否则置0。由于从int转换为bool会测试该值是否不为0,因此如果设置了i
的第b
位,则if语句的主体将执行。
答案 2 :(得分:0)
它使用整数作为二进制字符串。第n位为1表示此子集中存在第n个元素。类似地,为0表示它不存在于子集中。 & (1<<i)
用于检查第i位是否为1(换句话说,是否设置了第i位)