M×N (1≤M,N≤12) 的矩形草地,每格標記為肥沃(1)或貧瘠(0)。需選擇若干肥沃格種植玉米,滿足:
M ≤ 12 → 每行狀態用 int 位圖表示 (0~2^M-1)dp[row][state]:前 row 行,第 row 行狀態為 state 的方案數// 條件 1:無相鄰 1 (無連續種植)
bool validState(int s) {
return (s & (s << 1)) == 0;
}
// 條件 2:僅在肥沃地種植
bool matchLand(int s, int landRow) {
return (s & ~landRow) == 0; // s 的 1 必須是 landRow 的子集
}
相鄰兩行狀態 s1 (上行)、s2 (當前行) 需滿足:
(s1 & s2) == 0dp[i][s2] = Σ dp[i-1][s1],對所有合法 s1 求和。
#include <bits/stdc++.h>
using namespace std;
const int MOD = 100000000;
int M, N;
int land[13]; // 每行肥沃狀態位圖
vector validStates; // 所有合法狀態
int dp[13][1<<12]; // dp[row][state]
int main() {
cin >> M >> N;
for (int i = 1; i <= M; i++) {
int rowMask = 0;
for (int j = 0; j < N; j++) {
int x; cin >> x;
if (x == 1) rowMask |= (1 << j);
}
land[i] = rowMask;
}
// 預處理所有合法狀態
for (int s = 0; s < (1 << N); s++) {
if (validState(s)) validStates.push_back(s);
}
// 初始化第 0 行(虛擬行,狀態為 0)
dp[0][0] = 1;
for (int i = 1; i <= M; i++) {
for (int s2 : validStates) {
if (!matchLand(s2, land[i])) continue;
for (int s1 : validStates) {
if ((s1 & s2) == 0) { // 垂直不相鄰
dp[i][s2] = (dp[i][s2] + dp[i-1][s1]) % MOD;
}
}
}
}
// 答案:最後一行所有合法狀態之和
long long ans = 0;
for (int s : validStates) {
ans = (ans + dp[M][s]) % MOD;
}
cout << ans << endl;
return 0;
}
O(2^N),N≤12 時最大 4096O(1.618^N) (斐波那契數列),N=12 時約 377 個O(M * |valid|²) ≈ 12 × 377² ≈ 1.7×10⁶,極快O(M * 2^N) 可優化為滾動數組 O(2^N)s2 存儲兼容的 s1 列表,避免重複位運算dp[2][1< 節省空間 本題是狀態壓縮 DP 的**入門經典**,掌握「位圖表示行狀態 + 逐行轉移 + 兼容性檢查」模板,可解決大量網格選擇/放置類問題。