4×4 棋盤,每格黑/白。操作:選一格,翻轉它及其上下左右相鄰格。目標:全白或全黑。求最少步數,無解輸出 "Impossible"。
16 元 16 方程,消元後可能有自由變元。枚舉自由變元的 0/1 組合(最多 2^4=16 種),回代得特解,統計 1 的個數(操作次數),取最小值。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 16;
int a[N][N+1], x[N];
int gauss(int target) { // target=0 全白, 1 全黑
memset(a, 0, sizeof(a));
// 建立係數矩陣:每格影響自身及四鄰域
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j) {
int id = i*4 + j;
a[id][id] = 1;
if (i) a[id][(i-1)*4+j] = 1;
if (i<3) a[id][(i+1)*4+j] = 1;
if (j) a[id][i*4+j-1] = 1;
if (j<3) a[id][i*4+j+1] = 1;
}
// 讀入初始狀態,構建常數項
char s[5];
for (int i = 0; i < 4; ++i) {
cin >> s;
for (int j = 0; j < 4; ++j)
a[i*4+j][N] = (s[j] == 'b') ^ target; // 目標全白: b->1需翻; 目標全黑: w->1需翻
}
// 高斯消元 (GF(2))
int row = 0;
for (int col = 0; col < N && row < N; ++col) {
int sel = row;
while (sel < N && !a[sel][col]) ++sel;
if (sel == N) continue;
swap(a[sel], a[row]);
for (int i = 0; i < N; ++i)
if (i != row && a[i][col])
for (int j = col; j <= N; ++j) a[i][j] ^= a[row][j];
++row;
}
// 檢查無解
for (int i = row; i < N; ++i) if (a[i][N]) return -1;
// 自由變元枚舉 (N-row 個)
int freeVar = N - row;
int ans = N+1;
for (int mask = 0; mask < (1 << freeVar); ++mask) {
memset(x, 0, sizeof(x));
// 將自由變元賦值(對應行)
for (int i = 0; i < freeVar; ++i)
x[row + i] = (mask >> i) & 1;
// 回代
for (int i = row - 1; i >= 0; --i) {
int col = 0;
while (col < N && !a[i][col]) ++col;
int sum = a[i][N];
for (int j = col + 1; j < N; ++j) sum ^= a[i][j] & x[j];
x[col] = sum;
}
int cnt = 0;
for (int i = 0; i < N; ++i) cnt += x[i];
ans = min(ans, cnt);
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
char s[5];
while (cin >> s[0]) {
for (int i = 1; i < 4; ++i) cin >> s[i];
int ans1 = gauss(0), ans2 = gauss(1);
if (ans1 == -1 && ans2 == -1) cout << "Impossible\n";
else if (ans1 == -1) cout << ans2 << '\n';
else if (ans2 == -1) cout << ans1 << '\n';
else cout << min(ans1, ans2) << '\n';
}
return 0;
}
經典的「Lights Out」變體,核心是將翻轉操作的線性疊加建模為 GF(2) 方程組。雙目標(全白/全黑)分別建模取最小解。固定小規模時高斯消元+自由變元枚舉是標準解法。