参考博客 https://blog.csdn.net/so_so_y/article/details/76098713
题意
有一些灯泡组成了5*6的方阵。每个开关(开关会使灯泡的状态发生变化)除了控制它本身的灯泡以外,还同时控制上下左右四个方向的灯泡(如果有的话)。给出灯泡的初始状态,如果想吧所有的灯泡都关闭,应该按下哪些开关。0代表灯泡关闭,1代表灯泡打开。
分析
高斯消元解决开关问题的模板题。
这一类开关问题可以先转化为异或方程组,然后通过高斯消元解线性方程的方法进行求解。我们先来看一下该怎么转化。
它给出了初始的状态,我们把它看作一个n*m 01的矩阵L,开灯为1,关灯为0。那么目标状态也很明确,是一个n*m的全为0的矩阵。
每个开关控制的灯泡最多有五个(上下左右和它本身),我们把每个开关控制的灯泡都转换成矩阵,控制到的为1,没有控制到的为0。比如说如果灯泡组成的方阵使2*3的,那么一共有6个开关,每个开关所控制的矩阵(A1-A6)为
开关1: 1 1 0 开关5: 0 1 0 开关6:0 0 1
1 0 0 1 1 1 0 1 1
每个开关只有两种状态,我们用1代表按下开关,0代表不安。开关的状态显然也是一个n*m的01矩阵,只不过是未知的(也是我们要求的)。
现在我们把上面这些状态通过异或组成方程
L xor A1*X xor A2*X xor A3 *X.....A6*X =0 注意:这里不是矩阵乘法,是点乘。
然后两遍同时异或L可以变成
A1*X xor A2*X xor A3*X ...A6*X=L
然后可以拆成
A1(1,1)*X(1,1) xor A2(1,1)*X(1, 2)....A6(1,1)*X(2,3)=L(1,1)
。
。
。
A1(5,6)*X(1,1) xor A2(5,6)*X(1,2)....A^(5,6)*X(2,3)=L(5,6)
现在我们就已经把题目转化为了一个线性的异或方程组。接下来通过高斯消元求解。
高斯消元求解异或方程组和求解普通方程组基本一样,就两个地方有一些区别。一个是消元的时候,普通方程组是将每个系数减去关键方程的系数和当前方程的比值和值的乘积(绕口令?),而异或方程组直接将每个系数和关键方程异或就好。另一个是回带求ans的时候,好吧说不太清楚,具体可以看代码。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath> using namespace std;
const int maxn=;
const int dx[]={,,,,-};
const int dy[]={,,-,,};
int a[maxn][maxn],ans[maxn];
int T,n,m;
void Gauss(){
int k,col;
for(k=,col=;k<n*m&&col<=n*m;k++,col++){
int i=k;
for(int j=k+;j<=n*m;j++){
if(fabs(a[j][col])>fabs(a[i][col]))i=j;
}
if(i!=k){
for(int j=;j<=n*m+;j++){
swap(a[k][j],a[i][j]);
}
}
if(a[k][col]==){
--col;
continue;
}
for(i=k+;i<=n*m;i++){
if(a[i][col]){
for(int j=;j<=n*m+;j++){
a[i][j]^=a[k][j];//是这样子吧?
}
}
}
}
for(int i=n*m;i>=;i--){
ans[i]=a[i][n*m+];
for(int j=i+;j<=n*m;j++){
ans[i]^=(ans[j]&&a[i][j]);
}
}
return;
}
int main(){
n=,m=;
scanf("%d",&T);
for(int t=;t<=T;t++){
memset(a,,sizeof(a));
memset(ans,,sizeof(ans));
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
int cnt=(i-)*m+j;
scanf("%d",&a[cnt][n*m+]);
}
}
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
for(int k=;k<;k++){
int nx=i+dx[k];
int ny=j+dy[k];
if(nx>=&&nx<=n&&ny>=&&ny<=m){
int cnt=(i-)*m+j;
a[(nx-)*m+ny][cnt]=;
}
}
}
}
Gauss();
printf("PUZZLE #%d\n",t);
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
int cnt=(i-)*m+j;
printf("%d ",ans[cnt]);
}
printf("\n");
}
}
return ;
}