1. 程式人生 > 其它 >《演算法競賽進階指南》0x00習題---飛行員兄弟題解

《演算法競賽進階指南》0x00習題---飛行員兄弟題解

題目描述

“飛行員兄弟”這個遊戲,需要玩家順利的開啟一個擁有 16 個把手的冰箱。

已知每個把手可以處於以下兩種狀態之一:開啟或關閉。

只有當所有把手都開啟時,冰箱才會開啟。

把手可以表示為一個 \(4×4\) 的矩陣,您可以改變任何一個位置 \([i,j]\) 上把手的狀態。

但是,這也會使得第 \(i\) 行和第 \(j\) 列上的所有把手的狀態也隨著改變。

請你求出開啟冰箱所需的切換把手的次數最小值是多少。

輸入格式

輸入一共包含四行,每行包含四個把手的初始狀態。

符號 + 表示把手處於閉合狀態,而符號 - 表示把手處於開啟狀態。

至少一個手柄的初始狀態是關閉的。

輸出格式

第一行輸出一個整數 \(N\)

,表示所需的最小切換把手次數。

接下來 \(N\) 行描述切換順序,每行輸出兩個整數,代表被切換狀態的把手的行號和列號,數字之間用空格隔開。

注意:如果存在多種開啟冰箱的方式,則按照優先順序整體從上到下,同行從左到右開啟。

資料範圍

\(1≤i,j≤4\)

樣例

輸入樣例:
-+--
----
----
-+--
輸出樣例:
6
1 1
1 3
1 4
4 1
4 3
4 4

思路

  • 二進位制列舉對 \(16\) 個位置的操作,低位 從上到下,從左到右 到高位。
  • 同樣將初始狀態init對映成二進位制形式如樣例 0010000000000010 右邊是最低位。
  • 將行列操作對映成對一個數進行異或。比如第一行便是^= 0000000000001111
    ,第二行便是將低位 \(4\)\(1\)\(4\)
  • 再比如對第一列相當於 ^=0001000100010001,對第二列操作就是將第一列操作左移 \(1\)
  • 注意同時對行列操作需要將交叉的部分再異或一下即 ^=(row[x] & col[y])
  • 至此記錄更新的答案和操作,再將數還原為行列座標即可

時間複雜度

\(O(2^{16} * 16)\)

C++ 程式碼

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 5, M = 16;
int ans, init, op;
// + 代表 1, - 代表 0, 每一行下移要 << 4, 每一列右移就 << 1
int row[4] = {15, 15 << 4, 15 << 8, 15 << 12}, col[4] = {4369, 4369 << 1, 4369 << 2, 4369 << 3};

int gen(int x, int y){      // 對第 x 行、第 y 列進行操作
    return row[x] ^ col[y] ^ (row[x] & col[y]);
}

void change(int st){
    int t = init, tmp = st, res = 0, cnt = 0;
    while(tmp){
        if(tmp & 1){
            int x = cnt / 4, y = cnt % 4;
            t = t ^ gen(x, y);
            res ++;
        } 
        tmp >>= 1;
        cnt ++;
    }
    if(!t && res < ans){            // 更新答案
        op = st;
        ans = res;
    }
}

int main(){
    int cnt = 0;
    ans = 1000;
    for(int i = 0; i < 4; i++){
        string s;
        cin >> s;
        for(int j = 0 ; j < s.size(); j++)
            if(s[j] == '+') 
                init += 1 << (i * 4 + j);
    }
    for(int i = 0; i < 1 << M; i++)     // 二進位制列舉操作方案
        change(i);
    cnt = 0;
    cout << ans << endl;
    while(op){
        if(op & 1)
            cout << cnt / 4 + 1 << " " << cnt % 4 + 1 << endl;
        op >>= 1;
        cnt ++;
    }
    return 0;
}