poj1222(高斯消元法解异或方程组+开关问题)
作者:互联网
题目链接:https://vjudge.net/problem/POJ-1222
题意:给定一个5×6的01矩阵,改变一个点的状态时它上下左右包括它自己的状态都会翻转,因为翻转2次等价与没有翻转,那么每个点要么不翻转,要么翻转一次,求最终要怎样翻转可以使得矩阵全0。
思路:
做法1(枚举): 因为数据小,可以枚举第一行的所有可能,共1<<6种,之后的每一行都根据上一行决定,然后通过判断最后一行是否满足条件来判断这种方案是否可行。
做法2(高斯消元法): 为了说的清楚,现在假定矩阵为2×3,比如为,现在要达到的目标状态是:
现在要求6个灯的状态,相当于6个变量。另外有6个方程,分别表示6个点最终的状态。
比如,对于第0个灯,能影响到它的就是第0、1、3盏灯,因此它的方程是: (1*x0) ^ (1*x1) ^ (0*x2) ^ (1*x3) ^ (0*x4) ^ (0*x5) =0,(式子右边的0表示第0盏灯最开始的状态)。
同理第1盏灯的方程即为:(1*x0) ^ (1*x1) ^ (1*x2) ^ (0*x3) ^ (1*x4) ^ (0*x5) =1。
因此可以列出6个方程,然后用高斯消元法来求解,异或方程组的处理和普通线性方程组是一样的。
对于题目就是5×6=30个方程,30个变量分别表示30盏灯的状态。
AC代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> using namespace std; const int maxn=40; int T,cas,a[maxn][maxn],ans[maxn]; void init(){ memset(a,0,sizeof(a)); memset(ans,0,sizeof(ans)); for(int i=0;i<5;++i){ for(int j=0;j<6;++j){ int t=i*6+j; a[t][t]=1; if(i>0) a[t][t-6]=1; if(i<4) a[t][t+6]=1; if(j>0) a[t][t-1]=1; if(j<5) a[t][t+1]=1; } } } void Gauss(int equ,int var){ int r=0,c=0; for(;r<equ&&c<var;++r,++c){ int Maxr=r; for(int i=r+1;i<equ;++i) if(abs(a[i][c])>abs(a[Maxr][c])) Maxr=i; if(a[Maxr][c]==0){ --r; continue; } if(Maxr!=r){ for(int i=c;i<=var;++i) swap(a[r][i],a[Maxr][i]); } for(int i=r+1;i<equ;++i){ if(a[i][c]==0) continue; for(int j=c;j<=var;++j) a[i][j]^=a[r][j]; } } for(int i=var-1;i>=0;--i){ ans[i]=a[i][var]; for(int j=i+1;j<equ;++j) ans[i]^=(a[i][j]&ans[j]); } } int main(){ scanf("%d",&T); while(T--){ init(); for(int i=0;i<30;++i) scanf("%d",&a[i][30]); Gauss(30,30); printf("PUZZLE #%d\n",++cas); for(int i=0;i<5;++i){ for(int j=0;j<6;++j){ printf("%d",ans[i*6+j]); if(j!=5) printf(" "); } printf("\n"); } } return 0; }
标签:int,Maxr,异或,maxn,ans,include,poj1222,高斯消,翻转 来源: https://www.cnblogs.com/FrankChen831X/p/11769946.html