題目鏈接:點擊打開鏈接
題意:
給一個 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;
}