眾所周知,高斯消元可以用來求 $n$ 元一次方程組的,主要思想就是把一個 $n*(n+1)$ 的矩陣的對角線消成 $1$,除了第 $n+1$ 列(用來存放 $b$ 的)的其他全部元素消成 $0$,是不是聽起來有點不可思議??!
$NO NO NO!$
這不就是初中學的代入消元和加減消元嘛,思路一樣的。
$Step 1:$將所給出的 $n$ 元一次方程組的每個未知數系數和等號后面的常數寫成一個 $n*(n+1)$ 的矩陣
比如這個三元一次方程組我們就可以寫成如下 $3×4$ 的矩陣:
$Step 2 :$運用矩陣的各種性質,來將矩陣消成對角線上的元素為 $1$,並且除了第 $n+1$ 列其余元素均為 $0$ 的矩陣,
這樣我們就很容易的得出每個未知數的值:分別是從上到下第 $n+1$ 列的值(因為這時候每個未知數的系數都為 $1$)
那么是神馬神奇性質吶???找度娘啊
(1) 任意交換矩陣的兩行或兩列,矩陣不變;
(2)矩陣任意行或列 $a_i$ 加上或減去任意 $k$ 倍的任意行或列($a_i$ 行也可以加減 $k$ 倍的 $a_i$ 行),矩陣不變;
………………………………
其余的性質這里就用不到啦,這兩條性質足矣。
好啦,下面說一下怎么個消法(重點 嚶嚶嚶~)
以上面的矩陣為例:
明確我們的目的:把矩陣消成對角線為 $1$,除了第 $n+1$ 列其余元素都為 $0$。
也就是說,每一列都至少有一個元素不為 $0$,若有一列全為 $0$ 肯定有第 $i$ 行第 $i$ 列消不成 $1$,此時無解
不理解的話也可以從方程組的數學角度來思考一下:
我們把每個未知數的系數寫成矩陣,所以矩陣的某一列就是某一未知數的全部系數,
如果全為 $0$,那么不就是沒有這個未知數嗎?那么這個未知數的值就不能確定了,那不就是無解嗎?
知道了這個,我們就可以對這個矩陣進行初步判定:
for(int i=1;i<=n;i++) { pl=i; //從第i行開始往下找,一直找到一個第i列不為0的行 while(a[pl][i]==0&&pl<=n) pl++; // 判斷第i列元素非0的最上行,因為第i行第i列元素不能為0 if(pl==n+1) {cout<<"No Solution";return 0;} //一直判到了n+1行,可是一共才只有n行,說明有一列全為0,無解 for(int j=1;j<=n+1;j++) //將第i行元素與第pl行第i列不為0的那一行與當前行交換 swap(a[i][j],a[pl][j]); //保證第i行第i列不為0 }
這樣一來,我們就保證了第 $i$ 行第 $i$ 列的元素不為 $0$,可是我們要讓第 $i$ 行第 $i$ 列的值整成 $1$ 啊,我們可以用性質 $(2)$,讓第i行的每個元素都除以第 $i$ 行第 $i$ 列的值
注意:這里用到了除法,就有可能出現小數,所以我們要用 $double$ 類型定義二維數組矩陣
double k=a[i][i]; //讓第i行每個元素都除以a[i][i]使得a[i][i]為1 for(int j=1;j<=n+1;j++) a[i][j]=a[i][j]/k; //將第i行第i列的元素消成1,注意同行進行同樣的操作
我們就讓第 $i$ 行第 $i$ 列的元素搞成 $1$ 列,繼續完成接下來的任務:順便把第 $i$ 列的其他元素搞成 $0$;
我們已經把第 $i$ 行的搞成了$1$,所以我們只要把其余行的每個元素都減去本行的首元素*第 $i$ 行的對應元素(為什么是第 $i$ 行呢?仗着第 $i$ 行第 $i$ 列的元素是 $1$ 比較好消)
for(int j=1;j<=n;j++) { if(i!=j) //將第i列除了第i行的元素全消成0 { //方法是第j行每個元素a[j][m]都減去a[j][1]*a[i][m] double ki=a[j][i]; for(int m=1;m<=n+1;m++) a[j][m]=a[j][m]-ki*a[i][m]; } }
到這里就 $OK$ 啦,最后輸出第 $n+1$ 列的元素就是每個未知數的解啦!
完整代碼如下:
#include<iostream> #include<cstdio> #include<cmath> using namespace std; int n,pl; double a[1001][1001]; int main() { cin>>n; for(int i=1;i<=n;i++) for(int j=1;j<=n+1;j++) cin>>a[i][j]; for(int i=1;i<=n;i++) { pl=i; while(a[pl][i]==0&&pl<=n) pl++; // 判斷第i列首元素非0的最上行,因為第i行第i列元素不能為0 if(pl==n+1) {cout<<"No Solution";return 0;} //一直判到了n+1行,可是一共才只有n行,說明有一列全為0,無解 for(int j=1;j<=n+1;j++) //將第i行第i列元素不為0的那一行與當前行交換 swap(a[i][j],a[pl][j]); double k=a[i][i]; //讓第i行每個元素都除以a[i][i]使得a[i][i]為1 for(int j=1;j<=n+1;j++) a[i][j]=a[i][j]/k; //將第i行第i列的元素消成1,注意同行進行同樣的操作 for(int j=1;j<=n;j++) { if(i!=j) //將第i列除了第i行的元素全消成0 { //方法是第j行每個元素a[j][m]都減去a[j][1]*a[i][m] double ki=a[j][i]; for(int m=1;m<=n+1;m++) a[j][m]=a[j][m]-ki*a[i][m]; } } } for(int i=1;i<=n;i++) printf("%.2lf\n",a[i][n+1]); return 0; }
大家一定躍躍欲試了吧,給大家推薦一個洛谷板子題,鞏固一下吧。