传送门
题意
有5*6=30个的灯,每次拨动某个灯的开关,那么这个灯的上下左右的灯以及它本身的状态会发生变化,现在给出初始状态,要求也输出一个5*6的矩阵,1表示这个灯的开关被操作过,0表示没有,问能使灯全灭的操作是怎么样的
思路
首先一定要清楚一点:如果一个开关按动两次,那么就相当于没有操作
假设初始状态是全灭的,我们的目标状态是到题目中的初始状态,那么相同的操作是可以将题目中的初始状态变回灯是全灭的状态的
对于某个灯,能影响它状态变化的有它本身的开关和它上下左右的开关,那么每个灯我们实际都可以列出一个方程,题目数据有些多,我们可以拿3*3的举例子:
我们设最终状态为
b1
b
1
-
b9
b
9
,设
x1
x
1
-
x9
x
9
分别是第一个灯到第九个灯操作次数,当然结果只有1和0,也就是操作过、没操作过,可以影响第一个灯的状态的开关有第1、2、4开关,那么对于第一个灯来说,方程为:
1∗x1+1∗x2+0∗x3+1∗x4+...+0∗x9=b1
1
∗
x
1
+
1
∗
x
2
+
0
∗
x
3
+
1
∗
x
4
+
.
.
.
+
0
∗
x
9
=
b
1
相应的对应第二个灯来说,方程为:
1∗x1+1∗x2+1∗x3+0∗x4+1∗x5+...+0∗x9=b2
1
∗
x
1
+
1
∗
x
2
+
1
∗
x
3
+
0
∗
x
4
+
1
∗
x
5
+
.
.
.
+
0
∗
x
9
=
b
2
那么我们可以得到一个增广矩阵:
只是由于 b1 b 1 - b9 b 9 也只有0、1,所以我们解的时候要对2取余,根据方程解出来的 x1 x 1 - x9 x 9 就是结果了
由这个例子我们可以看出来一个开关对应着一个方程,所以本题就是一个30*31的增广矩阵,表示出来矩阵中哪些项是1,哪些是0,就可以了
代码
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int MAXN=35;
int a[MAXN][MAXN];//增广矩阵
int x[MAXN];//解集
bool free_x[MAXN];//标记是否是不确定的变元
inline int gcd(int a,int b)
{
int t;
while(b!=0)
{
t=b;
b=a%b;
a=t;
}
return a;
}
inline int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
int quick_mod(int a,int b,int mod)
{
int ans=1;
while(b)
{
if(b&1)
ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
int Gauss(int equ,int var,int MOD)
{
int i,j,k;
int max_r;
int col;
int ta,tb;
int LCM;
int temp;
int free_x_num;
int free_index;
for(int i=0;i<=var;i++)
{
x[i]=0;
free_x[i]=true;
}
col=0;
for(k = 0;k < equ && col < var;k++,col++)
{
max_r=k;
for(i=k+1;i<equ;i++)
{
if(abs(a[i][col])>abs(a[max_r][col])) max_r=i;
}
if(max_r!=k)
{
for(j=k;j<var+1;j++) swap(a[k][j],a[max_r][j]);
}
if(a[k][col]==0)
{
k--;
continue;
}
for(i=k+1;i<equ;i++)
{
if(a[i][col]!=0)
{
LCM = lcm(abs(a[i][col]),abs(a[k][col]));
ta = LCM/abs(a[i][col]);
tb = LCM/abs(a[k][col]);
if(a[i][col]*a[k][col]<0)tb=-tb;
for(j=col;j<var+1;j++)
{
a[i][j] = ((a[i][j]*ta-a[k][j]*tb)%MOD+MOD)%MOD;
}
}
}
}
for (i = var - 1; i >= 0; i--)
{
temp = a[i][var];
for (j = i + 1; j < var; j++)
{
if (a[i][j] != 0) temp -= a[i][j] * x[j];
temp=(temp%MOD+MOD)%MOD;
}
// while (temp % a[i][i] != 0) temp+=MOD;
// x[i] =( temp / a[i][i])%MOD ;
x[i]=temp*quick_mod(a[i][i],MOD-2,MOD)%MOD;
}
return 0;
}
int pow_m(int a,int n,int MOD)
{
int ret=1;
int temp=a%MOD;
while(n)
{
if(n&1){ret*=temp;ret%=MOD;}
temp*=temp;
temp%=MOD;
n>>=1;
}
return ret;
}
int main()
{
int T;
int cnt=0;
scanf("%d",&T);
while(T--)
{
cnt++;
memset(a,0,sizeof(a));
for(int i=0;i<5;i++)
for(int j=0;j<6;j++)
{
if(i>=1) a[6*i+j][6*(i-1)+j]=1;
if(i<=3) a[6*i+j][6*(i+1)+j]=1;
if(j>=1) a[6*i+j][6*i+j-1]=1;
if(j<=4) a[6*i+j][6*i+j+1]=1;
a[6*i+j][6*i+j]=1;
scanf("%d",&a[6*i+j][30]);
}
Gauss(30,30,2);
printf("PUZZLE #%d\n",cnt);
int k=0;
for(int i=0;i<30;i++)
{
k++;
if(k%6==0)
printf("%d\n",x[i]);
else printf("%d ",x[i]);
}
}
return 0;
}