题目链接:http://poj.org/problem?id=3279
题意:0代表白色,1代表黑色,每翻一个瓷片,上下左右的瓷片会跟着一起翻,要求翻动次数最小并且答案的字典排序最小。答案是一个矩形,代表的意思是对应位置的瓷片翻动几次,如果不存在就输出IMPOSSIBLE。
思路:
因为翻动一个瓷片会影响四周的瓷片,所以要一层一层的完成翻动,比如说我要把第一层的全部翻成白色,最好的办法是翻动第二层的瓷片,间接的翻动第一层的瓷片,而且还不会影响第一层的其他瓷片,如果倒数第二层的瓷片都翻完,最后一层还有黑色的瓷片,那么这种方案就是不可行的。
原本以为第一层的不动,从第二层开始翻就能得出答案,但事实上,第一层不动,如果方案不可行,未必就没有其他方法。所以我们要枚举第一层的所有情况,因为 M <= 15,所以第一层有 2^15种情况,只需要枚举第一层的情况,然后再做上面的操作求出答案就行了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <set>
#include <queue>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int Maxn = 1e5+10;
char G[20][20], ans[20][20], tmp[20][20], g[20][20];
int N, M, cnt;
void filp(int i, int j) {
if(i > 0) g[i-1][j] = g[i-1][j] == '1' ? '0' : '1';
if(j > 0) g[i][j-1] = g[i][j-1] == '1' ? '0' : '1';
if(j+1 < M) g[i][j+1] = g[i][j+1] == '1' ? '0' : '1';
if(i+1 < N) g[i+1][j] = g[i+1][j] == '1' ? '0' : '1';
g[i][j] = g[i][j] == '1' ? '0' : '1';
}
int main(void)
{
cin >> N >> M;
for(int i = 0; i < N; ++i)
for(int j = 0; j < M; ++j)
cin >> G[i][j];
int cnt = INF;
for(int i = 0; i < (1<<M); ++i) {
memset(tmp, 0, sizeof(tmp));
memcpy(g, G, sizeof(g));
int ct = 0;
for(int j = M-1; j >= 0; --j) {
if((i>>(M-1-j)&1) == 1) {
tmp[0][j] = '1';
filp(0, j); ct++;
} else tmp[0][j] = '0';
}
for(int ii = 1; ii < N; ++ii) {
for(int jj = 0; jj < M; ++jj) {
if(g[ii-1][jj] == '1') {
filp(ii, jj);
tmp[ii][jj] = '1';
ct++;
} else tmp[ii][jj] = '0';
}
}
bool ok = true;
for(int ii = 0; ii < M; ++ii) if(g[N-1][ii] == '1') ok = false;
if(ok && ct < cnt) {
cnt = ct; memcpy(ans, tmp, sizeof(ans));
}
}
if(cnt == INF) cout << "IMPOSSIBLE" << endl;
else {
for(int i = 0; i < N; ++i) {
for(int j = 0; j < M; ++j) {
cout << ans[i][j];
if(j < M-1) cout << " ";
}
cout << endl;
}
}
return 0;
}