状态压缩dp学习
以 P1879 为例
#include <bits/stdc++.h>
using namespace std;
const int M = 1e9;
int m, n, f[13][4096], F[13], field[13][13];
// max state: (11111111111)2 = (4095)10
bool state[4096];
int main()
{
//状态压缩dp学习(代码非原创,注释都是自己写的,看了好多篇博客,感觉状压dp看代码比较好理解)
//状压dp,本质上是一种暴力枚举,没有优化时空复杂度,而是利用位运算,方便了代码的书写(不然要写好多循环和判断)
// 既然是二进制位运算,又没有优化复杂度,那么它通常用于取/不取(0/1)且问题规模很小的问题,比较好识别(大概)
//01背包也可以状态压缩
//部分位运算
// a & b 存在同时为1的情况,不为0; 用于判断上下相邻。
// j & f==j 时 j的1包含于f的1; 用于和题目自身状态进行比较
// a & 1 取最后一位
// <<左移 乘2;
// >>右移 除以2 并舍去取整
// 左移 右移可用于判断状态合法性(左右相邻)。
cin >> m >> n;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
cin >> field[i][j];//原始场地
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
F[i] = (F[i] << 1) + field[i][j];//场地转换为状态
// F[i]: state on line i
int MAXSTATE = 1 << n;
for (int i = 0; i < MAXSTATE; i++)
state[i] = ((i&(i<<1))==0) && ((i&(i>>1))==0);//判断当前状态是否合法(左右不相邻)
f[0][0] = 1;
for (int i = 1; i <= m; i++)
for (int j = 0; j < MAXSTATE; j++)
if (state[j] && ((j & F[i]) == j))//状态合法(左右合法 并且 符合原场地要求)
for (int k = 0; k < MAXSTATE; k++)
if ((k & j) == 0)//上下状态不相邻 (上下合法)
f[i][j] = (f[i][j] + f[i-1][k]) % M;
int ans = 0;
for (int i = 0; i < MAXSTATE; i++)
ans += f[m][i], ans %= M;
cout << ans << endl;
return 0;
}