题解 P1879 [USACO06NOV]Corn Fields G

题目描述:

Link

给定一个 \(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;
}

posted @ 2021-02-22 16:32  recollector  阅读(46)  评论(0编辑  收藏  举报