状压dp
简介
状压dp是一种将一堆 \(0\) 和 \(1\) 压缩成一个二进制的dp,具体如下:
\(dp_{0/1,0/1,\dots,0/1}\rightarrow dp_{x}\)
这里的 \(x\) 是一个整数,但我们会把他看作是他的二进制形式。虽然时间复杂度没有变,但写起来更方便。状压dp一般适用于 \(N \le 20\) 的数据范围。
优化
由于某些状压dp十分卡常,所以这里介绍一些技巧:
方法1
如果枚举每一位是只需枚举为 \(1\) 的位,可以这样写:
int lowbit(int x) {
return x & -x;
}
int x = s;
for(; x; x -= lowbit(x)) {
int j = __builtin_ffs(x);
//...
}
其中 lowbit(x)
可以 \(O(1)\) 求 \(x\) 二进制下最低位的 \(1\) 的位权,具体用法我在这篇文章中介绍了,而 __builtin_ffs(x)
是 C++ 自带的 \(O(1)\) 求 \(x\) 二进制下最低位 \(1\) 的位号 \(+1\),这样写就只会枚举到 \(1\) 的位。
方法2
如果你需要求一个数二进制下 \(1\) 的个数,可以用这个:__builtin_popcount(x)
。这个也是 C++ 自带的 \(O(1)\) 求解的函数。
方法3
如果你要枚举一个数 \(y\),使得 \(x \operatorname{or} y = x\),则你可以这么枚举:
for(int y = x; y; y = (y - 1) & x) {
//...
}
//当y=0时...
这样可以保证所有 \(y\) 中二进制下的 \(1\),在 \(x\) 的二进制下同样有,即 \(x \operatorname{or} y = x\)。但这样的话就不能再循环中处理 \(y=0\) 的情况,只能在循环外处理。
方法4
如果你要枚举一个数 \(y\),使得 \(x \operatorname{or} y = y\),则你可以这么枚举:
for(int y = x; y < (1 << n); y = (y + 1) | x) {
//...
}
//当y=0时...
这样可以保证所有 \(x\) 中二进制下的 \(1\),在 \(y\) 的二进制下同样有,即 \(x \operatorname{or} y = y\)。