飞行员兄弟(AcWing116)题解——二进制状压在指数型枚举的应用

感觉不写题解容易忘。

题目链接

首先这道题有如下性质:

  1. 一个开关最多只操作一次,多次操作没有意义;
  2. 能成功的方案中,被按下的开关的顺序任意。

而这道题目,由于只是一个 \(4\times 4\) 的矩阵,所以方案数一共 \(2^{16}=65536\) 种。这么小的一个数据为枚举提供了条件。

于是,我们将 \(16\) 个开关用一个二进制数存储起来, \(0\) 表示关而 \(1\) 表示开。目标状态就是整数 \(0\)

以样例为例:

-+--
----
----
-+--

初始状态就可以用 \((10000000000010)_2\) (注意第 \(15\) 与第 \(16\) 个开关为关闭状态,故在高位的两个数未写出)来表示。

接下来,考虑每个开关对其他开关的状态的影响。对于开关 \((i,j)(0\leq i,j\leq3 )\),其被按下时会按 “十子” 对其他开关造成影响,也就是转换状态。我们只需要让对应开关异或开关本身所对应的数即可。因此我们引入一个 \(change[i][j]\),将会被影响的开关所对应的数的值加进该数组后即可,因为一个开关只会被对应数位影响。

开关所对应的值,就是第几个开关,例如开关 \((2,3)\) 表示第 \(11\) 个开关。也就是说,开关 \((i,j)(0\leq i,j\leq3 )\) 表示的是第 \(4i+j\) 个开关。而将第 \(a\) 个开关转化为开关 \((i,j)\) 利用 $i=a% 4 $ 与 \(j=a/4\) 即可。

最后枚举 \(2^{16}\) 种方案,如果可行(即最终状态用整数表示为 \(0\) )且方案数最少就是答案。

对于一个方案,例如 \((1100001)_2\),它表示 \(0,5,6\) 这三个编号的开关被按下。即状态异或上对应的 \(change\) 数组。

总结一下,整体思路就是:

将初始状态进行存储后,预处理 \(change\) 数组为后面的改变做准备,再枚举状态来对开关进行操作即可。

代码如下:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
char m[5][5];
int chag[5][5];
typedef pair<int,int> PII;
vector<PII> res;

int num(int x,int y){
    return x*4+y;
}

signed main(){
    int sta=0;
    for(int i=0;i<4;++i)
        for(int j=0;j<4;++j){
            cin>>m[i][j];
            if(m[i][j]=='+')
                sta+=(1<<num(i,j));		//存储初始状态
        }
    for(int i=0;i<4;++i){
        for(int j=0;j<4;++j){
            for(int k=0;k<4;++k){
                chag[i][j]+=(1<<num(i,k));
                chag[i][j]+=(1<<num(k,j));
            }
			chag[i][j]-=(1<<num(i,j));		//预处理 change 数组
        }
    }
    for(int i=0;i<(1<<16);++i){		//枚举状态
        int now=sta;
        vector<PII> path;
        for(int j=0;j<16;++j){		//枚举每个数位
            if((i>>j)&1){		//如果该数位为 1,说明对应的开关该按下
                int x=j/4,y=j%4;
                now^=chag[x][y];		//进行开关操作
                path.push_back({x,y});
            }
        }
        if(!now&&(res.empty()||res.size()>path.size()))
            res=path;
    }
    cout<<res.size()<<endl;
    for(auto p:res) cout<<p.first+1<<' '<<p.second+1<<endl;
    return 0;
}
posted @ 2024-07-13 20:28  wxdd  阅读(37)  评论(0)    收藏  举报