题意:一块(N*M) 的长方形土地,每一个格子上如果是1,则可以种菜;如果是0,则不能种菜.要求任意两块种菜的土地都不能有公共边.求总方案数.
分析:貌似跟炮兵阵地差不多.设(f[i][j]) 表示前i行,第i行状态是j的总方案数.则(f[i][j]+=f[i-1][k]) (状态j,k每一位上的1对应到长方形土地中都是1,且状态j,k的任意两个为1的位不相邻,且j&k!=1)
我们可以预处理出集合(S[i]) ,满足状态(S[i]) 的任意两个为1的位不相邻.这样就可以直接枚举有用的状态,优化一下时间.(但其实好像没什么必要,时间复杂度没有优化多少,但是代码长度增加了十几行)
对于条件"状态j,k每一位上的1对应到长方形土地中都是1",写一个check函数就好了.
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
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;
}
const int mod=100000000;
int m,n,sum,ans;
int S[5005],a[15][15],f[15][5005];
inline bool check(int x,int y){
if((!x)||(!y))return 1;
for(int i=0;i<n;i++)
if(((y>>i)&1)&&(a[x][n-i]==2))return 0;
//状态为1的位对应到长方形是不能种的地
return 1;
}
int main(){
m=read(),n=read();
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++){
a[i][j]=read();
if(!a[i][j])a[i][j]=2;//把不能种的地标记为2
}
//预处理集合S:
for(int i=0;i<(1<<n);i++){
int last=-1,bj=1;
for(int j=0;j<n;j++){
if((i>>j)&1){
if(last==-1)last=j;
else if(j-last<=1){
bj=0;
break;
}
else last=j;
}
}
if(bj)S[++sum]=i;
}
//初始化:
for(int i=1;i<=sum;i++)
if(check(1,S[i]))f[1][S[i]]=1;
for(int i=2;i<=m;i++){
for(int j=1;j<=sum;j++){
if(!check(i,S[j]))continue;
for(int k=1;k<=sum;k++){
if(!check(i-1,S[k])||(S[k]&S[j]))continue;
f[i][S[j]]+=f[i-1][S[k]],f[i][S[j]]%=mod;
}
}
}
for(int i=1;i<=sum;i++)if(check(m,S[i]))ans+=f[m][S[i]];
printf("%d
",ans%mod);
return 0;
}