POJ1222 EXTENDED LIGHTS OUT 高斯消元入门

21 篇文章 0 订阅
12 篇文章 0 订阅

题目链接:POJ1222

参考来源:大佬的题解

关于这个知识点我翻了许多博客,最后找到这一篇讲的十分清楚,所以转在这里。部分代码加了自己的注释,方便大家更容易看明白。

题意:给出一个5*6的图,每个灯泡有一个初始状态,1表示亮,0表示灭。每对一个灯泡操作时,会影响周围的灯泡改变亮灭,问如何操作可以使得所有灯泡都关掉。

思路:因为每盏灯,如果操作两次就相当于没有操作,所以相当于(操作次数)%2,即异或操作。

考虑一个2*3的图,最后需要的状态是 :,如果初始状态为:。对这两个矩阵的每个数字做异或操作可以得到线性方程组每个方程的答案。

总共6盏灯,0-5。那么可以列出6个方程。

对于第0盏灯,会影响到它的是第0, 1, 3盏灯,因此可以列出方程1*x0 + 1*x1 + 0*x2 + 1*x3 + 0*x4 + 0*x5= 0。

对于第1盏灯,会影响到它的是第0, 1, 2,4盏灯,因此可以列出方程1*x0 + 1*x1 + 1*x2 + 0*x3 + 1*x4 + 0*x5 = 1。

对于第2盏灯,会影响到它的是第1, 2, 5盏灯,因此可以列出方程0*x0 + 1*x1 + 1*x2 + 0*x3 + 0*x4 + 1*x5 = 0。

.....

所以最后可以列出增广矩阵:

然后用高斯消元求这个矩阵的解就可以了。

AC代码:

/*
2017年8月18日21:35:13
POJ1222
高斯消元解异或方程组模板
AC 
*/
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;

const int maxn=8;
int a[maxn*maxn][maxn*maxn];
int x[maxn*maxn];
int mp[maxn][maxn];


void init(){
	memset(a,0,sizeof(a));
	for(int i=0;i<5;i++){
		for(int j=0;j<6;j++){
			int pos=i*6+j;
			a[pos][pos]=1;//当前位置 
			if(j>0) a[pos][i*6+j-1]=1;//当前位置的左边 
			if(j<5) a[pos][i*6+j+1]=1;//当前位置的右边 
			if(i>0) a[pos][(i-1)*6+j]=1;//当前位置的上边 
			if(i<4) a[pos][(i+1)*6+j]=1;//当前位置的下边 
		}
	}
}

void Gauss(int n,int m){
	memset(x,0,sizeof(x));
	int r,c;
	for(r=0,c=0;r<n&&c<m;r++,c++){
		int max_r=r;
		for(int i=r+1;i<n;i++){
			if(fabs(a[i][c])>fabs(a[max_r][c]))	max_r=i;
		}
		if(a[max_r][c]==0){
			r--;
			continue;//处理当前行的下一个位置,或者说下一列 
		}
		if(max_r!=r){
			//交换这两行从第C列开始的元素,包括第m行 
			for(int i=c;i<m+1;i++) swap(a[max_r][i],a[r][i]);
		}
		for(int i=r+1;i<n;i++){
			if(a[i][c]==0) continue;//如果该行第一个元素的值为0,跳过
			for(int j=c;j<m+1;j++) a[i][j]=a[i][j]^a[r][j]; 
		} 
	}
	for(int i=m-1;i>=0;i--){
		x[i]=a[i][m];
		for(int j=i+1;j<m;j++){
			x[i]=x[i]^(a[i][j]&&x[j]);
		}
	}
} 

int main(){
	int t,kase=0;
	scanf("%d",&t);
	while(t--){
		init();
		for(int i=0;i<5;i++){
			for(int j=0;j<6;j++){
				scanf("%d",&a[i*6+j][30]);
			}
		}
		
		Gauss(30,30);
		printf("PUZZLE #%d\n",++kase);
		for(int i=0;i<5;i++){
			for(int j=0;j<6;j++){
				if(!j) printf("%d",x[i*6+j]);
				else printf(" %d",x[i*6+j]);
			}printf("\n");
		}
		
		
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值