這道題做的真糾結,這是學習高斯消元的第一題,沒想到就……,開始想了很久沒想到怎麼做,然後看一些題解吧,結果題解也沒看懂。主要是不明白爲什麼那樣列方程,爲什麼有唯一解,搜了很多博客加上考研線代殘留的知識終於完全明白了。
題意就不說了(此題需要一些線性代數的知識),我們先解決第一個問題怎樣列方程(或者爲什麼列方程)? 我們可以把 5*6 的初始矩陣看成一個30 * 1的列向量(爲了後面更好的列方程,所以看成列向量)Y(y1 ,y2 ,y3 ……y30) 。然後把每一個開關也看成一個列向量C(k)(向量裏總共30個值),那向量裏的值是什麼呢?如果第 k 個開關被按下影響了哪個燈就把相應位置爲1,否則置爲0(例如:按第一個開關,1,2,7號燈會有影響,那麼C(1) = {1 ,1 ,0 ,0 ,0 ,0 ,1 ,0,後面全爲0},其它的開關也這樣,那麼要是全部的開關關掉,就必須 Y(y1 ,y2……y30) + x1 * C(k1) + x2 * C(k2) + x3 * C(k3) ……+x30 * C(k30) = (0 ,0 ,0……0) (再次強調等式裏的向量都是列向量),這裏等式左邊需要mod 2,其中等式中的 x1 ,x2 ……x30 爲未知數,爲0 或 1 ,1 代表操作了此按鈕,0代表沒操作,然後我們把所有未知數看成一個列向量X(x1 ,x2 ,x3……x30),C(k1) ,C(k2) ……C(k30),也組成一個30*30 的矩陣 A(C(k1) ,C(k2) ,C(k3)……C(k30))(這裏Y其實是一個對稱向量,Y的轉置等於Y),這樣等式就變成了 A*X = Y (mod 2) ,這樣方程就列出來了。
然後爲什麼有唯一解呢?我們可以發現 r(A) = 30,也就是說 A 可逆,因爲r(A) = r(A ,Y) = 30 = n (注意 : 如果矩陣不是30 * 30 的這種形式的矩陣,則可能不是唯一解) ,所以方程一定有唯一解。好了下面就可以用高斯消元解方程了。(因爲此矩陣 A 爲對稱矩陣那麼代碼中完全可以將向量C(k) 組成 A矩陣的時候看成行向量)。
代碼:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std ;
const int MX = 30 + 10 ;
int a[MX][MX] ,ans[MX] ;//a數組存增廣矩陣,ans存最終答案
int x[] = {-1 ,1 ,0 ,0 ,0} ,y[] = {0 ,0 ,-1 ,1 ,0} ;
void Gauss(int n ,int m){
int max_r ;
int k = 0 ,col = 0 ;
for( ;k < n && col < m ; ++k ,++col){
max_r = k ;
for(int i = k + 1 ;i < n ; ++i)
if(abs(a[max_r][col]) < abs(a[i][col]))
max_r = i ;
if(!a[max_r][col]){
k-- ;
continue ;
}
if(max_r != k){
for(int i = col ;i <= m ; ++i)
swap(a[k][i] ,a[max_r][i]) ;
}
for(int i = k+1 ;i < n ; ++i){
if(a[i][col]){
for(int j = col ; j <= m ; ++j)
a[i][j] = a[i][j]^a[k][j] ;
}
}
}
for(int i = n-1 ; i >= 0 ; --i){
ans[i] = a[i][m] ;
for(int j = i+1 ;j < m ; ++j)
ans[i] = ans[i]^(a[i][j]&&ans[j]) ;
}
}
void Input(int n ,int m){
for(int i = 0 ;i < n ; ++i){
for(int j = 0 ;j < m ; ++j){
int t = i*m + j ;
scanf("%d" ,&a[t][n*m]) ;
for(int k = 0 ;k < 5 ; ++k){
int sx = x[k] + i ;
int sy = y[k] + j ;
int st = sx*m + sy ;
if(sx >= 0 && sx < n && sy >= 0 && sy < m){
a[t][st] = 1 ;
}
}
}
}
}
int main(){
//freopen("input.txt" ,"r" ,stdin) ;
int T ,cse = 1 ;
cin>>T ;
while(T--){
int n = 5 ,m = 6 ;
memset(a ,0 ,sizeof(a)) ;
memset(ans ,0 ,sizeof(ans)) ;
Input(n ,m) ;
Gauss(n*m ,n*m) ;
cout<<"PUZZLE #"<<cse++<<endl ;
for(int i = 0 ;i < n*m ; ++i){
if((i+1)%6 == 0){
printf("%d\n" ,ans[i]) ;
}else printf("%d " ,ans[i]) ;
}
}
return 0 ;
}
如果還不懂可以看一下這個博客 博客鏈接~~