POJ 1222 EXTENDED LIGHTS OUT(高斯消元)

題目鏈接~~~

 這道題做的真糾結,這是學習高斯消元的第一題,沒想到就……,開始想了很久沒想到怎麼做,然後看一些題解吧,結果題解也沒看懂。主要是不明白爲什麼那樣列方程,爲什麼有唯一解,搜了很多博客加上考研線代殘留的知識終於完全明白了。

 題意就不說了(此題需要一些線性代數的知識),我們先解決第一個問題怎樣列方程(或者爲什麼列方程)? 我們可以把 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 ;
}
如果還不懂可以看一下這個博客 博客鏈接~~
發佈了274 篇原創文章 · 獲贊 50 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章