《算法竞赛进阶指南》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;
}
posted @ 2022-03-10 10:38  Roshin  阅读(51)  评论(0编辑  收藏  举报
-->