题目描述
- FJ 购买了一处肥沃的矩形牧场,分成 M N(1<=M<=12; 1<=N<=12) 个格子。他想
在那里的一些格子中种植美味的玉米。遗憾的是,有些格子区域的土地是贫瘠的,不能耕
种。 - 精明的 FJ 知道奶牛们进食时不喜欢和别的牛相邻,所以一旦在一个格子中种植玉米,
那么他就不会在相邻的格子中种植,即没有两个被选中的格子拥有公共边。他还没有最终
确定哪些格子要选择种植玉米。 - 作为一个思想开明的人,FJ 希望考虑所有可行的选择格子种植方案。由于太开明,他
还考虑一个格子都不选择的种植方案!请帮助 FJ 确定种植方案总数。
输入格式
- 第一行包括两个用空格分隔的整数 M 和 N。
- 接下来的 M 行,每行 N 个数,描述了每行格子是否可以种植的情况(1 表示肥沃的、
适合种植,0 表示贫瘠的、不可种植)。
输出格式
- 输出一个整数,表示 FJ 可选择的方案总数 mod 108 的结果。
数据规模与约定
- 对于 60% 的数据,1<=M; N<=6
- 对于 100% 的数据,1 <=M; N<=12
题解
- 对于这道题,数据比较弱的情况下,暴力枚举dfs能够拿到90分。
- 每一行都存在2^n中情况需要进行枚举,可以为0。当前行的状态为上一行所有状态的总和。
- 即dp(i,j)=∑dp(i-1,k)(k=0 to (1<< n)-1)。
- 第i行第j种情况在合法条件下,所得总情况是第i-1行所有合法情况之和。
- 第i行与第i-1行的情况要再次判断。初始化只需要进行判断当前是否合法即可。
- 压缩第i行的状态为j,能够通过枚举当前行和上一行来计算总和。时间复杂度(O)m(log2(n))^2。
上代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
void f(){
freopen("cowfood.in","r",stdin);
freopen("cowfood.out","w",stdout);
}
int m,n;
long long dp[13][4100];
int map[13][13];
bool check(int x,int y){
for (int i=n-1;i>=0;i--){
if(map[x][n-i]==0&&(y>>i)&1==1)//判断当前行为符合都在肥沃的土壤内
return false;
}
for (int i=n-1;i>=0;i--){
if((((y>>i)&1)==1)&&(((y>>i)&(y>>(i+1)))==1)) return false;//判断相邻的位置上是否有种植
}
return true;
}
int main(){
f();
cin>>m>>n;
memset(map,false,sizeof(map));
memset(dp,0,sizeof(dp));
for (int i=1;i<=m;i++){
for (int j=1;j<=n;j++)
cin>>map[i][j];
}
for (int j=0;j<=(1<<n)-1;j++){
if(check(1,j))
dp[1][j]=1;//初始化第1行只需要判断合法情况
}
int ans=0;
for (int i=2;i<=m;i++)//层数
for (int j=0;j<=(1<<n)-1;j++)//枚举第i行的状态
if(check(i,j))//判断当前状态合法
for (int k=0;k<=(1<<n)-1;k++)//枚举第i-1行的状态
if((j&k)==0) //如果第i行与第i-1行都互相错开则&运算都为0
dp[i][j]=(dp[i][j]+dp[i-1][k])%100000000;
for (int i=0;i<=(1<<n)-1;i++){
ans+=dp[m][i];//统计最后一行的情况个数
ans=ans%100000000;
}
cout<<ans;
}