题目链接:http://poj.org/problem?id=1753
题意:一个 4*4 的棋盘,初始时上面放满了黑色或白色的棋子.对 (i, j) 位置进行一次操作后 (i, j), (i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1) 位置的棋子会变成原来相反的状态.问最少需要多少步可以将棋盘上的棋子全部变成白色或者黑色.
思路:分别将棋子变成黑色和白色,然后再用高斯消元解,其中步数较小者即为答案.
注意不存在唯一解时需要枚举自由变元来取得最小步数.
代码:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std; const int inf = 1e9;
const int MAXN = 3e2;
int equ, var;//有equ个方程,var个变元,增广矩正行数为equ,列数为var+1,从0开始计数
int a[MAXN][MAXN];//增广矩正
int free_x[MAXN];//用来存储自由变元(多解枚举自由变元可以使用)
int free_num;//自由变元个数
int x[MAXN];//解集 int Gauss(void){//返回-1表示无解,0表示有唯一解,否则返回自由变元个数
int max_r, col, k;
free_num = ;
for(k = , col = ; k < equ && col < var; k++, col++){
max_r = k;
for(int i = k + ; i < equ; i++){
if(abs(a[i][col] > abs(a[max_r][col]))) max_r = i;
}
if(a[max_r][col] == ){
k--;
free_x[free_num++] = col;//这个是变元
continue;
}
if(max_r != k){
for(int j = col; j < var + ; j++){
swap(a[k][j], a[max_r][j]);
}
}
for(int i = k + ; i < equ; i++){
if(a[i][col] != ){
for(int j = col; j < var + ; j++){
a[i][j] ^= a[k][j];
}
}
}
}
for(int i = k; i < equ; i++){
if(a[i][col] != ) return -;//无解
}
if(k < var) return var - k;//返回自由变元个数
for(int i = var - ; i >= ; i--){
x[i] = a[i][var];
for(int j = i + ; j < var; j++){
x[i] ^= (a[i][j] && x[j]);
}
}
return ;
} const int n = ;
string s[]; int solve(void){
int op = Gauss();
if(op == -) return inf;//无解
else if(op == ){//存在唯一解
int sol = ;
for(int i = ; i < var; i++){
sol += x[i];
}
return sol;
}else{//存在多解,需要枚举自由变元找到最小需要的操作数
int sol = inf;
int tot = << op;//有op个变元,每个变元可取0或1,共有1<<op总情况
for(int i = ; i < tot; i++){//二进制枚举,i二进制位上为1的取1,为0的取0
int cnt = ;
for(int j = ; j < op; j++){
if(i & ( << j)){//当前第j位变元取1
x[free_x[j]] = ;
cnt++;
}else x[free_x[j]] = ;
}
for(int j = var - op - ; j >= ; j--){
int idx;
for(idx = j; idx < var; idx++){
if(a[j][idx]) break;
}
x[idx] = a[j][var];
for(int l = idx + ; l < var; l++){
if(a[j][l]) x[idx] ^= x[l];
}
cnt += x[idx];
}
sol = min(sol, cnt);
}
return sol;
}
} void f(int op){
for(int i = ; i < n; i++){
for(int j = ; j < n; j++){
int cnt = i * n + j;
if(s[i][j] == 'w') a[cnt][var] = - op;
else a[cnt][var] = op;
x[cnt] = ;
}
}
for(int i = ; i < equ; i++){//构造增广矩阵
int x1 = i / n;
int y1 = i % n;
for(int j = ; j < var; j++){
int x2 = j / n;
int y2 = j % n;
if(abs(x1 - x2) + abs(y1 - y2) < ) a[j][i] = ;
else a[j][i] = ;
}
}
} void gel(void){
int sol = inf;
f();
sol = min(sol, solve());
f();
sol = min(sol, solve());
if(sol == inf) cout << "Impossible" << endl;
else cout << sol << endl;
} int main(void){
while(cin >> s[]){
equ = var = n * n;
for(int i = ; i < n; i++){
cin >> s[i];
}
gel();
}
return ;
}