Luogu P1879玉米田题解

题目在这里喔

题目描述

农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)

输入输出格式

输入格式:

第一行:两个整数M和N,用空格隔开。
第2到第M+1行:每行包含N个用空格隔开的整数,描述了每块土地的状态。第i+1行描述了第i行的土地,所有整数均为0或1,是1的话,表示这块土地足够肥沃,0则表示这块土地不适合种草。

输出格式:

一个整数,即牧场分配总方案数除以100,000,000的余数。

思路

这个题因为数据范围很小啊 (1 <= N, M <= 12), 轻易发现应该是一道状压dp
那么再一看到这个

所有整数均为0或1

我们便可以想到使用2进制来进行状压

首先先来考虑一下:每一行的状态应该有(11111111111)2 = (4095)10种
那么使用一个dp[i][j]来记录第i行的每一种情况的总共的方案数

现在状态定义了出来,可是这个状态转移方程如何来写呢?

可以想到:枚举相邻的两行,一直向下推即可(是不是想到了滚动数组手动滑稽
于是便有了dp[i][j] = f[i][j] + f[i-1][k];
(其中i,j,k都是需要枚举的,也即是需要三重循环)
所以时间复杂度就为 O(N * 2 ^ (2 * N)) = O(2 ^ N),显然是可以过的

最后
我们只需要统计最后一行的所有情况的值即可。

代码

(因为数据较小,所以小编没有使用滚动数组,有兴趣的读者可以自己试试看)

#include <cstdio>
#include <cctype>
#pragma GCC optimize(2)
template <class T>
void r(T &x)
{
	#define gc getchar()
	x = 0;
	char c = gc;
	while (!isdigit(c)) c = gc;
	while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = gc;
}
const int N = 12;
const int mod = 100000000;
int n, m;
bool field[N + 5][N + 5];
int dp[N + 5][1 << N], farm[N + 5];
bool sta[1 << N];
int main()
{
	r(n), r(m);
	for (int i = 1;i <= n; i++)
		for (int j = 1;j <= m; j++)
			r(field[i][j]);
	for (int i = 1;i <= n; i++)
		for (int j = 1;j <= m; j++)
			farm[i] = (farm[i] << 1) + field[i][j];
	int tot = 1 << m;
	for (int i = 0;i < tot; i++)
		sta[i] = (!(i & (i << 1))) && (!(i & (i >> 1)));
	dp[0][0] = 1;
	for (int i = 1;i <= n; i++)
		for (int j = 0;j < tot; j++)
			if (sta[j] && ((j & farm[i]) == j))
				for (int k = 0;k < tot; k++)
					if (!(k & j))
						dp[i][j] = (dp[i][j] + dp[i - 1][k]) % mod;
	long long ans = 0;
	for (int i = 0;i < tot; i++)
		ans += dp[n][i];
	printf ("%lld", ans % mod); 
	return 0;
}

Thanks!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值