题目链接:http://poj.org/problem?id=1222
一,题意:
有一个5 * 6 的矩阵 矩阵里面每一个格子都有一个开关,开关只有两种状态,开与关。
我们用1表示开,0表示关,每一个开关的状态变化都会影起其上下左右开关的变化。
我们开始给出矩阵中所有开关的初始状态,问改变多少个开关的状态能使得所有开关变成关。
我们用1表示改变,用0表示不变。输出所有开关改变状态。
二,解析:
我们将所有开关是否改变设为一个变量xi,这样30个变量xi(i=1,2,,,,30)
我们必须列出30个方程才能解出来。对于每一个开关它的状态受到其上下左右开关变化的影响,
这样我们可以列出30个方程,对于每一个方程的结果是每个节点的改变次数。这个节点改变次数加上
该节点的初始状态模2一定要是0,这样就可以构建方程了。
总结:每个开关的关系图构成了方程的系数矩阵,,开关的初始状态就是其方程常数项。
注意:每个开关的是否反转只要两只状态所以这里消元是用到了异或运算。
由于30个方程30个未知数所以一定有唯一解,不会存在自由变量。
三;代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
const int Max=35;
int Mat[Max][Max];//增广矩阵
int graph[Max][Max];//图形
int N;
int X[5]= {0,1,0,-1};
int Y[5]= {1,0,-1,0};
void Swap(int x,int y)
{//交换x行与y行
for(int i=0; i<=30; i++)
swap(Mat[x][i],Mat[y][i]);
}
int gauss()
{
int row,col;
int key;
for(row=0,col=0; row<30 && col<=30; row++,col++)
{//row为行循环,,col为列循环
key=row;
for(int i=row; i<30; i++)
{//选取最大的主元由于这里只有0,1所以只要是1就是最大元
if(Mat[i][col]!=0)
{
key=i;
break;
}
}
if(key!=row)
Swap(key,row);
for(int i=0; i<30; i++)
{//利用主元消元
if(i!=row && Mat[i][col])
{//这里用疑惑主要原因是矩阵中只能有0,1.
for(int j= col; j<=30; j++)
Mat[i][j]=Mat[row][j]^Mat[i][j];
}
}
}
}
int main()
{
scanf("%d",&N);
for(int l=1;l<=N;l++)
{
memset(Mat,0,sizeof(Mat));
for(int i=0; i<5; i++)
{
for(int j=0; j<6; j++)
{
scanf("%d",&graph[i][j]);
Mat[i*6+j][30]=graph[i][j];//增广矩阵最后一列(常数列)
}
}
int key=0;
for(int i=0; i<5; i++)
{
for(int j=0; j<6; j++)
{//考虑每一个点及其相关联的点
Mat[key][key]=1;
int x,y;
for(int k=0; k<4; k++)
{//四个方向(改点受四个相邻点的影响)
x=i+X[k];
y=j+Y[k];
if(x>=0 && y>=0 && x<5 && y<6)
{
Mat[key][x*6+y]=1;
}
}
key++;
}
}
gauss();
int s=0;
printf("PUZZLE #%d\n",l);
for(int i=0; i<5; i++)
{//高斯消元后最后一列就是结果
cout<<Mat[s++][30];
for(int j=1; j<6; j++)
cout<<" "<<Mat[s++][30];
cout<<endl;
}
}
return 0;
}