在状态压缩时,注意考虑数学上集合之间的关系:
交集:a&b
并集:a|b
对称差:a异或b
差集:a&~b
包含:a属于b时 ,a&b = a or a| b = b
集合与元素的关系:
全集:{0,1,……,n-1} 二进制表示为 (1<<n)-1如果存在,那一位为1
补集:~s or ((1<<n)-1)异或s
属于:(s>>i)&1=1
不属于:(s>>i)&1 !=1
添加:s |(1<<i)
删除:s&~(1<<i)
删除最后一个元素:
#include<iostream>
using namespace std;
int main()
{
//删除最小元素
int s = 4;//101
//s - 1 100
//cout << (s&(s-1));
//101
//100
//=>100
return 0;
}
标准的函数库:
C++;
__builtin_popcount(s)返回s中二进制1的个数
二进制长度:32-__builtin_clz(s) //__builtin_clz(s) 返回从最高位 0到遇到第一个1的多少位
集合中的最小元素:__builtin_ctz(s)
只包含最小元素的子集,即二进制最低 11 及其后面的 00,也叫 lowbit (经常在树状数组中见到)
s = 10010
~s=01101
(~s)+1 = 01110 //根据补码的定义,这就是 -s 最低 1 左侧取反,右侧不变
s&-s =00010
遍历集合
for (int i = 0; i < n; i++) {
if ((s >> i) & 1) { // i 在 s 中
// 处理 i 的逻辑
}
}
设集合为 s,从大到小枚举 s 的所有非空子集 subsub:
for (int sub = s; sub; sub = (sub - 1) & s) {
// 处理 sub 的逻辑
}