P1879 [USACO06NOV] Corn Fields G

P1879 [USACO06NOV] Corn Fields G

首先考虑暴搜,枚举每个点种不种草,再判断是否有没有相邻的。

我们会发现,只有 \(n\) 个点对答案有影响,为点 \((i,j)\) 上方的点 \((i-1,j)\) 到点 \((i-1,n)\) 及点 \((i,1)\) 到点 \((i,j)\)。如下图所示,只有红色块会对黄色块影响(图丑,见谅)。

图

那么我们就可以状态压缩了,只记录位置以及红色块信息。这里定义 \(1\) 为种草,\(0\) 为不种草。令 \(f_{i,j,state}\) 为当前位置为点 \((i,j)\),红色块状态为 \(state\)(最低位到最高位为矩阵中从上到下,从左到右的顺序)。

若当前点要种草,有以下限制:

  • 输入矩阵的这一位必须为 \(1\)

  • 若点不在第一行,那么点上面一格必须为 \(0\)

  • 若点不在第一列,那么点左边一格必须为 \(0\)

若不种草,则没有限制,直接转移即可。

这类 dp 被称为轮廓线 dp。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 15, mod = 1e8;
int n, m, ans, a[N][N], f[N][N][1<<N];

int main() {
	ios::sync_with_stdio(0);
	cin >> m >> n;
	for (int i = 1; i <= m; ++i)
		for (int j = 1; j <= n; ++j)
			cin >> a[i][j];
	f[0][n][0] = 1;
	for (int i = 1; i <= m; ++i)
		for (int j = 1; j <= n; ++j) {
			int x = i, y = j-1;
			if (!y) --x, y = n;
			for (int k = 0; k < 1<<n; ++k) {
				f[i][j][k>>1] = (f[i][j][k>>1]+f[x][y][k])%mod;
				if (!a[i][j] || (k&1)) continue;
				if (j != 1 && (k>>n-1&1)) continue;
				f[i][j][(k>>1)|(1<<n-1)] = (f[i][j][(k>>1)|(1<<n-1)]+f[x][y][k])%mod;
			}
		}
	for (int i = 0; i < 1<<n; ++i) ans = (ans+f[m][n][i])%mod;
	cout << ans;
	return 0;
}
posted @ 2023-12-09 22:51  123wwm  阅读(48)  评论(0)    收藏  举报