大致题意
农场主在 N * M 的区域种植,部分区域因为贫瘠无法种植,且相邻区域不能同时种植。求可能的方案数,一个区域都不选择也算一个方案。1 ≤ M ≤ 12, 1 ≤ N ≤ 12
粗略估计,每一行都有2N种情况,都要向下一行的2N种状态转移。考虑到相邻区域并不能同时种植,实际状态数要小于2N,打表后就可以直接遍历了。dp实现上为了节省空间用了滚动数组。
在 0 ~ (1 << N) 范围内打表,只要 x & (x >> 1) 为 0 即可判断该状态无相邻元素。因为若存在相邻元素,左移或右移 1 位后与原值按位相与的结果一定不为 0 。这样保证了各元素在每一行是不相邻的。
贫瘠土地的位置按位输入,方便和当前状态 state 相与进行判断,若结果不为零,一定在某个位置两者同时为 1 ,与不能在贫瘠土地种植的条件相冲突。同样,为了判断各元素在列上是否相邻,可以通过当前行和前一行的状态相与来进行判断。
#include <cstdio>
#include <STDLIB.H>
#include <algorithm>
#include <iostream>
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define abs(x) ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define eps 1e-4
#define M_PI 3.14159265358979323846
#define MAX_M 12
#define MAX_N 12
using namespace std;
const int mod = 1e8;
int N, M, cnt;
int ft[MAX_M];
int dp[2][377]; //N = 12 时打表得到的状态数最大值
int state_table[377];
//int dp[2][1 << MAX_N];
//int state_table[1 << MAX_N];
void init(){
memset(ft, 0, sizeof(ft));
for(int i = 0; i < M; i++){
for(int j = 0; j < N; j++){
int f;
scanf("%d", &f);
//按照位来存储贫瘠土地位置,实际上应该是 ft[i] += 1 << (N - 1 - j);
//根据对称性,这样写结果一致
if(!f) ft[i] += 1 << j;
}
}
cnt = 0;
for(int s = 0; s < 1 << N; s++){
if(!(s & (s >> 1))) state_table[cnt++] = s;
}
}
void solve(){
memset(dp, 0, sizeof(dp));
//滚动数组
int *crt = dp[0], *next = dp[1];
for(int i = 0; i < cnt; i++){
if(!(state_table[i] & ft[0])) crt[i] = 1;
}
for(int r = 1; r < M; r++){
for(int i = 0; i < cnt; i++){
int res = 0, s1 = state_table[i];
if(s1 & ft[r]){
//因为复用数组,一定要保证个元素都被更新,漏了这句吃了一WA
next[i] = res;
continue;
}
for(int j = 0; j < cnt; j++){
int s2 = state_table[j];
if((s2 & ft[r - 1]) || (s1 & s2)) continue;
res = (res + crt[j]) % mod;
}
next[i] = res;
}
swap(crt, next);
}
int res = 0;
for(int i = 0; i < cnt; i++) res = (res + crt[i]) % mod;
printf("%d\n", res);
}
int main(){
while(~scanf("%d%d", &M, &N)){
init();
solve();
}
return 0;
}