状态压缩DP
前置知识:
1:基础DP
2:位运算的运用
何为状态压缩DP?
状态压缩
位运算
- 与运算
& (and ):相同位,有0 为0 ,其余为1 。 - 或运算
| (or ):相同位,有1 为1 ,其余为0 。 - 异或运算
^ (xor ):相同位,不同为1 ,相同为0 。 - 取反操作
~ (not ):0 变1 ,1 变0 。 - 左移操作
<< (shi ):a << b 是把二进制数a 向左(高位)移b 位(低位的最后b 位补0 )。 - 右移操作
>> (shr ):a >> b 是把二进制数a 向右(低位)移b 位(高位的起始b 位补0 )。
常用的位运算操作
- 取出二进制数
x 的第k 位,存在y 中:y = 1 << k &x ( 或y = x >> k & 1 ) - 取出二进制数
x 的最后一个1 (也就是lowbit ),存在y 中:y = x & -x - 将二进制数
x 的第k 位变为1 :x = x | 1 << k - 将二进制数
x 的第k 位变为0 :x = x & ~ (1 << k ) - 将二进制数
x 的第k 位取反:x = x ^ 1 << k - 取出二进制数
x 的最后k 位,存在y 中:y = x & (1 << k )- 1 - 将二进制数
x 低位连续的1 变为0 :x = x &x + 1 - 将二进制数
x 低位连续的0 变为1 :x = x | x - 1 - 取出二进制数
x 低位连续的1 ,存在y 中:y =(x ^ x + 1)>> 1 - 快速枚举集合
S (用二进制数x 表示)的所有子集:for(i = x;i != 0;i = (i - 1) &x))
如何应用?
LG1879 [USACO06NOV]Corn Fields G
题意:
给定一片牧场,分成
分析:
我们可以使用二进制来表示状态,
同时,我们令
状态转移方程则为
答案即为:所有的
那么我们如何求所有满足条件的
根据题意,
显然,贫瘠的土地无法种草。所以我们可以预处理所以肥沃的土地,通过位运算将其标记为
其次,相邻的土地无法种草。而相邻分为左右和上下两种,我们可以在转移的时候进行判断。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
const int MaxSize = 20;
char buf[1 << MaxSize], *p1, *p2;
#define GetChar() \
((p1 == p2) ? (p2 = buf + fread(p1 = buf, 1, 1 << MaxSize, stdin), p1 == p2 ? EOF : *p1++) : *p1++)
template <class T>
inline void in(T &x) {
x = 0;
register bool f = 0;
register char c = GetChar();
while (c < 47 || c > 58) {
f |= (c == '-'), c = GetChar();
}
while (c > 46 && c < 59) {
x = (x << 3) + (x << 1) + (c & 15), c = GetChar();
}
x = f ? (~x + 1) : x;
}
const int Mod = 1e8;
int m, n, Maxi, Ans, soil[15], f[15][1 << 15];
int main() {
in(m), in(n);
Maxi = (1 << n) - 1; // 上线
for (register int i = 1; i <= m; ++i)
for (register int j = 1; j <= n; ++j) {
int x;
in(x);
soil[i] = (soil[i] << 1) + x; // 预处理
}
f[0][0] = 1; // 边界条件
for (register int i = 1; i <= m; ++i) {
for (register int j = 0; j <= Maxi; ++j) {
if ((j & (j << 1)) != 0) // 左右相邻
continue ;
if ((j & soil[i]) != j) // 肥沃 or 贫瘠
continue ;
for (register int k = 0; k <= Maxi; ++k)
if ((k & j) == 0) // 上下相邻
f[i][j] = (f[i][j] + f[i - 1][k]) % Mod;
}
}
for (register int i = 0; i <= Maxi; ++i)
Ans = (Ans + f[m][i]) % Mod;
printf("%d\n", Ans);
return 0;
}
LG1896 [SCOI2005]互不侵犯
题意:
给定一个长度为
分析:
由题,可以发现,