LG P1879 [USACO06NOV]Corn Fields G

题目描述

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

分析

\(s_i\)为第\(i\)行土地的状态,对于其第\(j\)位,当这块土地适合种草时为\(0\),不适合种草时为\(1\)

用二进制状态\(j\)表示某一行种草的情况,某一位为\(1\)表示种草,否则不种。

当然,对于一个状态\(j\),它自身必须是合法的,即不存在相邻两位都为\(1\),可以表示为:\(\forall 0\le i\le n-2\)\((j\gg i)\&1=0\)\((j\gg (i-1))\&1=0\)。用\(O(2^n)\)的时间处理出满足条件的状态集合\(S\)

\(f(i,j)\)当前在处理第\(i\)行,且改行状态为\(j\)时的方案数。显然,只需考虑\(S\)内的状态\(j\)即可。枚举前一行的状态\(k\),那么要有\(j\&k=0\),即上下土地不能相邻。另一方面,\(j,k\)本身对于第\(i,i-1\)行也应当是合法的,即\(j\&s_i=0\)\(k\&s_{i-1}=0\),表示没有草种在不能种草的土地上。

综上所述,

\[f(i,j)\leftarrow \sum_{k\in S,k\&j=0,k\&s_{i-1}=0}f(i-1,k),\ j\&s_i=0,j\in S \]

边界条件\(f(0,0)=1\),答案即为\(\sum\limits_{j\in S}f(m,j)\)。预处理复杂度\(O(2^n)\),转移复杂度\(O(m|S|^2)\),统计复杂度\(O(|S|)\)。事实上\(|S|\)较小。

Code

#include <cstdio>
#include <iostream>

using namespace std;

const int Maxn=(1<<12)+3;;
const int Maxm=13;
const int Mod=1e9;

int f[Maxm][Maxn],s[Maxm],g[Maxn],g_cnt;
int n,m;

inline bool valid(int j)
{
	for(int i=0;i<(n-1);++i)
		if(((j>>i)&1)&&((j>>(i+1))&1))
			return false;
	
	return true;
}

int main()
{
	scanf("%d%d",&m,&n);
	
	for(int i=1;i<=m;++i)
	{
		int S=0;
		
		for(int j=0,k;j<n;++j)
			scanf("%d",&k),
			(!k)&&(S|=1<<j);
		
		s[i]=S;
	} 
	
	for(int i=0;i<(1<<n)-1;++i)
		if(valid(i))
			g[++g_cnt]=i;
	
	f[0][0]=1;
	
	for(int i=1;i<=m;++i)
		for(int j=1;j<=g_cnt;++j)
			for(int k=1;k<=g_cnt;++k)
			{
				int s1=g[j],s2=g[k];
				
				if(s1&s2||s1&s[i]||s2&s[i-1])
					continue;
				
				f[i][s1]=(f[i][s1]+f[i-1][s2])%Mod;
			}
	
	int ans=0;
	
	for(int j=1;j<=g_cnt;++j)
		ans=(ans+f[m][g[j]])%Mod;
	
	printf("%d\n",ans);
}
posted @ 2020-11-06 09:10  Anverking  阅读(104)  评论(0编辑  收藏  举报