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(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);
}