题意:农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。
遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。
John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)
Input
第一行:两个整数M和N,用空格隔开。
第2到第M+1行:每行包含N个用空格隔开的整数,描述了每块土地的状态。第i+1行描述了第i行的土地,所有整数均为0或1,是1的话,表示这块土地足够肥沃,0则表示这块土地不适合种草。
Output
一个整数,即牧场分配总方案数除以100,000,000的余数。
Sample Input
2 3
1 1 1
0 1 0
Sample Output
9
思路:可以参考这位大佬的博客,下面的代码是根据我自己的理解写的
详见代码
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int mod = 100000000;
int n, m;
int cur[20];//cur[i]表示第i行的状态
int dp[20][5000];//dp[i][j]表示前i行,第i行状态为j时的的方案数
int state[500];//表示可行状态集合
int top;
void init() {//预处理所有可行状态
int sum = 1 << m;
for(int i = 0; i < sum; i++) {
if(i & (i << 1)) continue;//让当前数字和左移一位进行比较,如果有连续的1,就continue
else state[++top] = i;
}
}
int fit(int i, int num) { //判断第num行能不能变成第i种可行状态,判断一行的
if(state[i]&cur[num]) return 0;
return 1;
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
int x;
scanf("%d", &x);
if(x == 0) cur[i] += 1 << (m - j);//预处理cur
/*
这里将原来数据为0的存储为1,原来是1的存储为0,
是为了方便与预处理后的状态进行对比
举个例子:原来数据是1 1 1 ——>存储后:0 0 0
然后这里m==3,所以预处理可行状态有5种,
分别是 0 0 0(1表示放了草,0表示没放草)
1 0 0
0 1 0
0 0 1
1 0 1
然后这里将存储后的 0 0 0 与5种可行状态进行位与操作
发现结果都是0,所以都是可行的,
换句话说就是看原草地能不能通过种植草变成当前对比的可行状态
*/
}
}
init();
for(int i = 1; i <= top; i++) {
if(fit(i, 1)) dp[1][i] = 1;//dp的初始化
}
for(int i = 2; i <= n; i++) { //遍历每一行
for(int j = 1; j <= top; j++) { //遍历可行状态
if(fit(j, i) == 0) continue;//如果当前行不能够变成第j种可行状态,那就判断下一种
for(int k = 1; k <= top; k++) {/*如果满足了当前行可以变成可行状态,
那么根据题目要求还必须要求上一层(i-1层)也必须是某一种可行状态,
同时第i层和第i-1层也不能有上下同一位置都为1的情况
*/
if(fit(k, i - 1) == 0) continue;//i-1层满足可行状态
if(state[j]&state[k])continue;//第i层和第i-1层的可行状态不能有冲突
dp[i][j] = (dp[i][j] + dp[i - 1][k]) % mod;//dp的更新
}
}
}
int ans = 0;
for(int i = 1; i <= top; i++) ans = (ans + dp[n][i]) % mod;
printf("%d\n", ans);
return 0;
}