状压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\)

posted @ 2024-04-16 17:26  Yaosicheng124  阅读(8)  评论(0编辑  收藏  举报