描述
农民 John 购买了一处肥沃的矩形牧场,分成M*N(1 <= M <= 12; 1 <= N <= 12)个 格子。他想在那里的一些格子中种植美味的玉米。遗憾的是,有些格子区域的土地是贫瘠的, 不能耕种。 精明的 FJ 知道奶牛们进食时不喜欢和别的牛相邻,所以一旦在一个格子中种植玉米,那么 他就不会在相邻的格子中种植,即没有两个被选中的格子拥有公共边。他还没有最终确定哪些 格子要选择种植玉米。
作为一个思想开明的人,农民 John 希望考虑所有可行的选择格子种植方案。由于太开明, 他还考虑一个格子都不选择的种植方案!请帮助农民 John 确定种植方案总数。
输入
Line 1: 两个用空格分隔的整数 M 和 N
Lines 2…M+1: 第 i+1 行描述牧场第i行每个格子的情况, N 个用空格分隔的整数,表示 这个格子是否可以种植(1 表示肥沃的、适合种植,0 表示贫瘠的、不可种植)
输出
Line 1: 一个整数: FJ 可选择的方案总数 除以 100,000,000 的余数。
样例输入
2 3
1 1 1
0 1 0
样例输出
9
提示
给可以种植玉米的格子编号:
1 2 3 4
只种一个格子的方案有四种 (1, 2, 3, 或 4),种植两个格子的方案有三种 (13, 14, 或 34),种植三个格子的方案有一种 (134),还有一种什么格子都不种。 4+3+1+1=9。
标签
usaco2007nov glod
简单状压dp。
直接预处理出所有合法情况。
f[i][j]f[i][j]f[i][j]表示前i行,第i行状态为sta[[j]sta[[j]sta[[j]时的总方案数。
这样就可以从上一行的合法状态转移过来了。
代码:
#include<bits/stdc++.h>
#define N 20
#define mod 100000000
using namespace std;
int n,m,sta[4005],ban[N],tot=0,f[N][4005],ans=0;
int main(){
scanf("%d%d",&n,&m);
int up=1<<m;
for(int i=0;i<up;++i)if(!(i&(i<<1)))sta[++tot]=i;
for(int i=1;i<=n;++i){
int tmp;
for(int j=1;j<=m;++j)scanf("%d",&tmp),ban[i]=ban[i]*2+tmp;
}
for(int i=1;i<=tot;++i)if((sta[i]|ban[1])==ban[1])f[1][i]=1;
for(int i=2;i<=n;++i){
for(int j=1;j<=tot;++j){
if((sta[j]|ban[i-1])!=ban[i-1])continue;
for(int k=1;k<=tot;++k){
if((sta[k]|ban[i])!=ban[i])continue;
if(sta[k]&sta[j])continue;
(f[i][k]+=f[i-1][j])%=mod;
}
}
}
for(int i=1;i<=tot;++i)(ans+=f[n][i])%=mod;
cout<<ans;
return 0;
}