题解 P1879 [USACO06NOV]Corn Fields G
题目描述:
给定一个 \(n \times m\) 的网格,有一些格子不能选,要求选出若干个格子,要求这些格子互相没有公共边,求方案数。
\(1 \leq n,m \leq 12\) 。
Solution
发现 \(n,m\) 很小,并且第 \(i\) 行的摆放只和第 \(i-1\) 行有关,考虑状压 \(dp\) 。
设 \(f_{i,j}\) 表示前 \(i\) 中第 \(i\) 行状态为 \(j\) 的摆放方案数。
属性: \(\mathcal{Count}\) 。
考虑怎么计算:限制有两个:有一些格子不能选,选出来的格子不能有公共边。
设 \(vaild(i,j)\) 表示 \(i,j\) 这两个二进制数表示的状态是否能放在一起(也就是有没有一个位置都是 \(1\) ,代码是 (i & j) == 0
) 。
那么 \(f_{i,j}=\sum_{vaild(k,j)} f_{i-1,k}\) 。
如果 \(j\) 这个状态本身就不合法(有公共边或者放了不能放的格子),那么 \(f_{i,j}=0\) 。
复杂度为 \(\mathcal{O}(n4^n)\) 。
如果只考虑没有公共边这个条件,状态其实不多,我们可以预处理出来(最多 \(377\) 个),时间复杂度就能降为 \(\mathcal{O}(n 377^2)\) 。
代码如下:
#include <cstdio>
#include <cstring>
#include <cctype>
inline int read() {
int num = 0 ,f = 1; char c = getchar();
while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar();
while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar();
return num * f;
}
const int N = 15 ,S = 400 ,mod = 1e8;
int s[N] ,f[N][S] ,n ,m ,st[S] ,idx;
signed main() {
n = read(); m = read();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
s[i] = s[i] << 1 | read();
for (int i = 0; i < (1 << m); i++)
if ((i & (i >> 1)) == 0) st[++idx] = i;
f[0][1] = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= idx; j++)
if ((s[i] & st[j]) == st[j])
for (int k = 1; k <= idx; k++)
if ((st[j] & st[k]) == 0)
f[i][j] = (f[i][j] + f[i - 1][k]) % mod;
int ans = 0;
for (int i = 1; i <= idx; i++)
ans = (ans + f[n][i]) % mod;
printf("%d\n" ,ans);
return 0;
}