AcWing_116 飞行员兄弟(组合状态的二进制表示、暴力枚举)

AcWing

116. 飞行员兄弟

对于每个把手,可以操作,也可以不操作(重复地操作同一个把手无效),一共有$2^16$种操作方案,每一种方案对应16个把手的操作用一个16位的二进制数表示,000...000 ~ 111...111,十进制即0 ~ 65535. 对于每一种操作方案,枚举二维的把手位置,映射到二进制方案的对应位上,判断其是否为1(表示操作),是则改变行列状态。注意在每一次操作方案迭代之前要复原数组。

#include <bits/stdc++.h>
using namespace std;
char g[4][5], backup[4][5];
int getoffs(int a, int b){
    return 4 * a + b;
}
void turnone(int a, int b){
    if(g[a][b] == '+') g[a][b] = '-';
    else g[a][b] = '+';
}
void turnall(int a, int b){
    for(int i = 0; i <= 3; i++) turnone(i, b);
    for(int i = 0; i <= 3; i++) turnone(a, i);
    turnone(a, b);
}
int main(){
    for(int i = 0; i <= 3; i++) cin >> backup[i];
    int res = 17, rop;
    for(int i = 0; i <= (1 << 16) - 1; i++){
        memcpy(g, backup, sizeof backup);
        int ops = 0;
        for(int u = 0; u <= 3; u++) for(int v = 0; v <= 3; v++){
            int offset = getoffs(u, v);
            if(i >> offset & 1){
                turnall(u, v);
                ops++;
            }
        }
        bool alllt = true;
        for(int u = 0; u <= 3; u++) for(int v = 0; v <= 3; v++) if(g[u][v] == '+'){
            alllt = false;
            break;
        }
        if(alllt && ops < res) res = ops, rop = i;
    }
    cout << res << endl;
    for(int u = 0; u <= 3; u++) for(int v = 0; v <= 3; v++){
        int offset = getoffs(u, v);
        if(rop >> offset & 1) cout << u + 1 << ' ' << v + 1 << endl;
    }
    return 0;
}