一开始是DFS暴力枚举,各种超时之后,无奈用了BFS+位运算:
(1)考虑到可能的操作过程(以某一位置为中心,翻转该行、该列和他本身)一共有2^16种,这是因为对某一位置不可能进行多次翻转(实际上等同于异或操作);
(2)由于矩阵有16位,用16bit的整形(1代表close,0代表open)即可表示当前矩阵的状态;
(3)对于以某一位置为中心的操作,相当于对当前状态进行一次和该位置mask的异或
所以整个解题过程可以分成三步:
(1)获得以每一位置为翻转中心时的mask
(2)读取输入矩阵
(3)BFS搜索
下面是AC代码
#include <cstdio>
#include <queue>
using namespace std;
/*
let 1 stand for closed
let 0 stand for open
let bit i stand for matrix[i / 4][i % 4]
*/
bool vis[1 << 16] = {0}; //record whether this state has been visited
int move[1 << 16] = {0};//record steps that have been done to reach this state
//if move[n].bit i is 1, then matrix[i / 4][i % 4] has been chosen to flip
int mask[16];
void initMask()
{
const int rowMask[4] = {
0x0F << 0,
0x0F << 4,
0x0F << 8,
0x0F << 12
};
const int colMask[4] = {
1 << 0 | 1 << 4 | 1 << 8 | 1 << 12,
1 << 1 | 1 << 5 | 1 << 9 | 1 << 13,
1 << 2 | 1 << 6 | 1 << 10 | 1 << 14,
1 << 3 | 1 << 7 | 1 << 11 | 1 << 15,
};
for(int i = 0; i < 16; ++i){
mask[i] = (1 << i) ^ rowMask[i / 4] ^ colMask[i % 4];
}
}
void printMove(int moves)
{
int step = 0;
for(int bit = 0; bit < 16; ++bit){
if(moves >> bit & 0x01) ++step;
}
printf("%d", step);
for(int bit = 0; bit < 16; ++bit){
if(moves >> bit & 0x01) printf("\n%d %d", bit / 4 + 1, bit % 4 + 1);
}
}
int main()
{
//step 0: init mask
initMask();
//step 1: input 4 * 4 matrix
int now = 0, bit = 0;
for(int i = 0; i < 4; ++i){
for(int j = 0; j < 4; ++j){
if(getchar() == '+') now |= 1 << bit;//this door is closed
++bit;
}
getchar();//skip '\n' at line end
}
//step 2: bfs to find minimum steps from start to ZERO
queue<int> q;
q.push(now);
vis[now] = true;
move[now] = 0;
while(!q.empty()){
for(int i = 0, n = q.size(); i < n; ++i){
now = q.front();
q.pop();
//choose one more bit that hasn't beeb choosen to flip
for(bit = 0; bit < 16; ++bit){
int already = move[now];
if(already >> bit & 0x01) continue;
int nex = now ^ mask[bit];
if(vis[nex]) continue;
move[nex] = already | (1 << bit);
if(nex == 0){
printMove(move[nex]);
return 0;
}
q.push(nex);
vis[nex] = true;
}
}
}
return 0;
}
实际上,如果令x[i]表示对matrix[i/4][i%4]位置是否进行flip操作,则有
x[0] ^ x[1] ^ x[2] ^ x[3] ^ x[4] ^ x[8] ^ x[12] = matrix[0][0]
...
x[12] ^ x[13] ^ x[14] ^ x[15] ^ x[3] ^ x[7] ^ x[11] = matrix[3][3]
此题还可以用高斯消元法来解,下面是AC代码:
#include <cstdio>
#include <algorithm>
using namespace std;
bool a[16][16 + 1];//extended matrix
bool x[16]; //answer vector
/*
After all operations we want matrix be to all ZERO matrix
Let x[i] stand for whether we flip matrix[i / 4][i % 4], then
x[0] ^ x[1] ^ x[2] ^ x[3] ^ x[4] ^ x[8] ^ x[12] = matrix[0][0]
...
x[12] ^ x[13] ^ x[14] ^ x[15] ^ x[3] ^ x[7] ^ x[11] = matrix[3][3]
*/
void initCofficient()
{
for(int i = 0; i < 16; ++i){
a[i][i] = true;//related to itself
int row = i / 4, col = i % 4;
for(int j = 0; j < 4; ++j){
a[i][row * 4 + j] = true;//related to its row
a[i][j * 4 + col] = true;//related to its col
}
}
}
void gauss()
{
int i, j, k;
for(i = 0; i < 16; ++i){
if(a[i][i] == 0){//find a row where a[*][i] = 1 and swap it with row i
for(j = i + 1; j < 16; ++j){
if(a[j][i]) break;
}
if(j == 16) continue;//no row has col i to be 1
//row j's col i is 1, swap row j with row i
for(k = 0; k < 17; ++k) swap(a[i][k], a[j][k]);
}
//use row i to eliminate rows below where a[*][i] = 1
for(j = i + 1; j < 16; ++j){
if(a[j][i]){
for(k = 0; k < 17; ++k) a[j][k] ^= a[i][k];
}
}
}
//backward induce the answer of upper triangle matrix
for(i = 15; i >= 0; --i){
x[i] = a[i][16];
for(j = 15; j > i; --j){
if(a[i][j]) x[i] ^= x[j];
}
}
}
int main()
{
//step 0: init cofficient
initCofficient();
//step 1: input matrix
int k = 0;
for(int i = 0; i < 4; ++i){
for(int j = 0; j < 4; ++j){
a[k++][16] = getchar() == '+';
}
getchar();//skip '\n' at line end
}
//step 3: gauss elimination method
gauss();
//step 4: print result
k = 0;
for(int i = 0; i < 16; ++i){
if(x[i]) ++k;
}
printf("%d", k);
for(int i = 0; i < 16; ++i){
if(x[i]) printf("\n%d %d", i / 4 + 1, i % 4 + 1);
}
return 0;
}