解题思路:关键是要理解,每个格子只有两种状态,而且最终翻转方法与格子被翻转的次序无关。
1. 位运算+BFS / BFS
2. 枚举:只要有一种枚举不满足,则其他枚举也不满足,反之,只要有一种枚举满足,则所有枚举都满足,因为同一个方块反转两次,整体不变,猜测所有的可行解都是可以相互转换,并且都始于全黑/白,终于全黑/白。
http://poj.org/showmessage?message_id=165833(0ms,用进制枚举的方法很好,但是整体的方法不能确定是否正确,因为这题只有一个case,但是思路很好)。
3. 高斯消元法
代码:
1. ^的使用, 两次^等于本身,和翻转一样。
2. 每个棋子只有两种状态, 共2 ^ 16种, 可以用一个int表示,即状态压缩,然后BFS枚举所有的状态。
3. Flip()是对压缩状态的翻转+4, -4, +1, -1
4. 在BFS的时候,队列中保存状态和层数的二元组,要比在队列中每层压入哨兵高效和简单。
5. 使用位移直接input到压缩状态。
//280k 79ms
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <queue>
#include <algorithm>
using namespace std;
int Flip(int cur, int pos) {
cur ^= (1 << pos);
if (pos + 4 < 16) cur ^= (1 << pos + 4);
if (pos - 4 >= 0) cur ^= (1 << pos - 4);
if (pos % 4) cur ^= (1 << pos - 1);
if (pos % 4 != 3) cur ^= (1 << pos + 1);
return cur;
}
int BFS(int board) {
if (board == 65535 || board == 0)
return 0;
queue<pair<int, int> > q;
bool visit[65536];
memset(visit, 0, sizeof(visit));
q.push(make_pair(board, 0));
visit[board];
while (q.size()) {
pair<int, int> pii = q.front();
q.pop();
int cur = pii.first;
for (int i = 0; i < 16; ++i) {
int next = Flip(cur, i);
if (next == 65535 || next == 0)
return pii.second + 1;
if (visit[next])
continue;
q.push(make_pair(next, pii.second + 1));
visit[next] = 1;
}
}
return -1;
}
int main() {
char piece;
int board = 0;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
piece = getchar();
if (piece == 'b')
board |= (1 << (i * 4 + j));
}
getchar();
}
int res = BFS(board);
if (res == -1)
printf("Impossible");
else
printf("%d", res);
return 0;
}