题目描述
农场主John新买了一块长方形的新牧场,这块牧场被划分成(m)行(n)列((1 ≤ m ≤ 12); (1 ≤ n ≤ 12)),每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。
遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。
John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)
分析
设(s_i)为第(i)行土地的状态,对于其第(j)位,当这块土地适合种草时为(0),不适合种草时为(1)。
用二进制状态(j)表示某一行种草的情况,某一位为(1)表示种草,否则不种。
当然,对于一个状态(j),它自身必须是合法的,即不存在相邻两位都为(1),可以表示为:(forall 0le ile n-2),((jgg i)&1=0)或((jgg (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_{kin S,k&j=0,k&s_{i-1}=0}f(i-1,k), j&s_i=0,jin S
]
边界条件(f(0,0)=1),答案即为(sumlimits_{jin 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
",ans);
}