蒟蒻初学状压DP,做题入门中。。。
概念:
状压DP,就是用进制的方法把多个位置的选取状态压缩成一个变量的动态规划
分析:
在这题中,根据数据范围,我们显然不可能开出 2 ^ 144的数组存储每一个状态,如果用二进制记录状态,所有位置的值无法被表示出来,但是一行12个值是可以被记录,可以被枚举完的
若我们从上往下确定情况的话,第i行的情况只会受到第i-1行的影响,第1~i-2行不会影响第i行,也就是无后效性,故我们可以按行枚举
设f[i][state]表示当前计算到第i行,且枚举第i行的种植情况为state的方案数
则 f[ i ] [ state ] = sigma f( i - 1 , state' ),也就是枚举i - 1行的子集就可以了
当然,根据题意,还存在很多的限制条件:
- 第i行的种植情况不能和原有的土地情况冲突
- state & g [ i ] == g [ i ]
- 第i行的state内部不能冲突,不能有左右相邻的1
- state & (state << 1) == 0 或 state & (state >> 1) == 0
- 第i行的state不能和第i-1行的state‘冲突,不能存在相邻两行有上下相邻的1
- state & state' == 0
那么最后的答案就是f [ m ] [ 0 ] + ... + f [ m ] [ 1 << n - 1 ]
#include <bits/stdc++.h>
const int mod = 1e8;
int n, m, x;
int f[15][1 << 12], g[15];
bool state[1 << 12];
int main()
{
scanf("%d%d", &m, &n);
int MaxState = 1 << n;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
scanf("%d", &x), g[i] = (g[i] << 1) + x;
for (int i = 0; i < MaxState; i++)
state[i] = ((i & (i << 1)) == 0) && ((i & (i >> 1)) == 0);
/* g[i]: state on line i */
f[0][0] = 1;
for (int i = 1; i <= m; i++)
for (int j = 0; j < MaxState; j++)
if (state[j] && ((j & g[i]) == j))
for (int k = 0; k < MaxState; k++)
if ((k & j) == 0)
f[i][j] = (f[i][j] + f[i - 1][k]) % mod;
int ans = 0;
for (int i = 0; i < MaxState; i++)
ans += f[m][i], ans %= mod;
std::cout << ans << '\n';
return 0;
}