P1879 [USACO06NOV] Corn Fields G

题目描述

农场主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

2 3
1 1 1
0 1 0

输出 #1

9

一道入门的状压dp题,但对于我这种萌新还是太难了,所以写篇博客纪念一下。。。

看到m的范围很小,我们就可以考虑对m进行状态压缩

我们设 \(f[i][j]\) 表示第\(i\)行放置情况为\(j\)的方案数.

我们就可以枚举所有的状态来进行转移

也就是 \(f[i][j] += f[i-1][k]\)

那怎么判断一个状态是否合法呢?

首先,这一行不能选相邻的两列也就是 j & (j<<1) == 0, j & (j>>1) == 0

并且还要与上一行的所选的不能有相邻的即 j & k == 0

那么怎么判断这个状态选的土地是否都合法呢?

我们可以预先处理每一行的初始状态,即对土地能不能选进行状态压缩。

当 s[i] & j == 0 就代表j这个状态可能选了不肥沃的土地。(可以画个图了解一下)

最终答案就是 \(\sum_{i=0}^{i< (1<<m)} f[n][i]\)

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int p = 1e8;
int n,m,ans,x,base[20],f[15][35000],zhuangtai[20],map[20][20];
inline int read()
{
	int s = 0, w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
	return s * w; 
}
bool judge(int x)//判断x这个状态是否合法
{
	if(x & (x<<1)) return 0;
	if(x & (x>>1)) return 0;
	return 1;
}
int main()
{
	n = read(); m = read();
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
		{
			map[i][j] = read();
		}
	}
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
		{
			zhuangtai[i] = (zhuangtai[i]<<1) + map[i][j];//把一开始土地能不能选的情况进行状态压缩
		}
	}
	base[0] = 1;
	for(int i = 1; i <= m; i++) base[i] = base[i-1] * 2;
	f[0][0] = 1;
	for(int i = 1; i <= n; i++)
        {
		for(int j = 0; j < base[m]; j++)
		{
			if(judge(j) && ((zhuangtai[i] & j) == j))//j这个状态不能选不肥沃的土地
			{
				for(int k = 0; k < base[m]; k++)
				{
					if(((j&k) == 0) && judge(k))
					{
						f[i][j] += f[i-1][k];//转移
						f[i][j] %= p;
					}
				}
			}
		}
	}
	for(int i = 0; i < base[m]; i++) ans = (ans + f[n][i]) % p;
	printf("%d\n",ans);
	return 0;
}
posted @ 2020-08-12 06:50  genshy  阅读(135)  评论(0编辑  收藏  举报