题目链接:点击打开链接
题意:
给一个 n * m 的矩阵;
其中 0 表示可以在此处种玉米;
1 则相反;
而农夫规定两株玉米不能种在一起;
即一株玉米的上下左右都没有其它玉米;
问有多少种种法;
其中不种也是一种方法;
理解:
状态压缩 dp;
递推式含义:dp[i][j] 表示在第 i 行的 j 状态有多少种方法;
递推式:dp[i][j] += dp[i - 1][k];
其中 j 要满足条件(0)(1),k 也要满足条件(0)(1)(2);
条件(0):
j 状态里的 1 的位置不相邻;
即:j & (1 << j) != 0;
条件(1):
原矩阵中的 0 的位置在 j 状态中不能为 1 ;
即:(w[i] & j) == 0;
其中 w[i] 表示第 i 行中数值为 0 的集合的一个整数,由二进制转化为十进制;
条件(2):
第 i - 1 行的 k 状态不能与第 i 行的 j 状态冲突;
即:v[j] & v[k] == 0;
其中 v[i] 表示符合条件(1)的 i 状态;
代码如下:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MIN_INF = 1e-7;
const int MAX_INF = (1e9) + 7;
#define X first
#define Y second
LL dp[13][1 << 13];
int v[1 << 13], w[13];
const int MOD = 100000000;
int main() {
int n, m;
while (cin >> n >> m) {
memset(dp, 0, sizeof(dp));
memset(v, 0, sizeof(v));
memset(w, 0, sizeof(w));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
int x;
cin >> x;
w[i] += (x == 0 ? (1 << j) : 0); // w[i] 存储的是 0 的位置,并将其转化为整数;
} // 其中状态与其 & 则可知该状态是否全在 1 位;
} // 若 & 后为 0 则全在 1 位;
// 若为 1 则存在 1 在 0 位的状态,则不符合题意;
int k = 0;
for (int i = 0; i < (1 << m); ++i) { //将所有状态转化为数字,并去掉相邻位都为 1 的状态;
if ((i & (i << 1)) == 0) {
v[k++] = i;
}
}
for (int i = 0; i < k; ++i) { // dp 初始值
if ((w[0] & v[i]) == 0) { // 判断该状态是否在 i 行可行;
dp[0][i] = 1; // 若为 1 则该状态没有全在 1 位,不符合题意;
}
}
for (int i = 1; i < n; ++i) {
for (int j = 0; j < k; ++j) {
if (w[i] & v[j]) { // 判断该状态是否在 i 行可行;
continue;
}
for (int l = 0; l < k; ++l) {
if (w[i - 1] & v[l]) { // 判断该状态是否在 i - 1 行可行;
continue;
}
if ((v[j] & v[l]) == 0) { //判断该状态与上一个状态是否冲突;
dp[i][j] = (dp[i][j] + dp[i - 1][l]) % MOD; //加了再 MOD;
}
}
}
}
LL ans = 0;
for (int i = 0; i < k; ++i) {
ans = (ans + dp[n - 1][i]) % MOD;
}
cout << ans % MOD << endl;
}
return 0;
}