[USACO06NOV]玉米田Corn Fields

洛咕

POJ

题意:一块\(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\n",ans%mod);
    return 0;
}

posted on 2019-06-04 21:19  PPXppx  阅读(107)  评论(0编辑  收藏  举报