POJ-2965(BFS + 位运算)

12 篇文章 0 订阅
2 篇文章 0 订阅

一开始是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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值