高斯消元學習筆記
高斯消元是一種用來求解線性方程組(多元一次方程組)的演算法。
假設我們現在需要求解一個n元一次方程:
\(\begin{cases} a_{1,1}x_1+a_{1,2}x_2+...+a_{1,n}x_n=b_1 \\ a_{2,1}x_1+a_{2,2}x_2+...+a_{1,n}x_n=b_2 \\ \vdots \\ a_{n,1}x_1+a_{n,2}x_2+...+a_{1,n}x_n=b_n \\ \end{cases}\)
把係數存下來。
\(\begin{bmatrix}a_{1,1}&a_{1,2}&\cdots&a_{1,n}&b_1\\\vdots&\ddots&\vdots\\a_{n,1}&a_{n,2}&\cdots&a_{n,n}&b_n\end{bmatrix}\)
然後我們就可以進行高斯消元了,具體方法是:
\(step 1\)
列舉每一行,
1.將第i行到第n行中第i列數的絕對值最大的數所在行與當前的第i行交換,防止當前行的第i列為0,消元無法 進行。
2.用第i行的數乘上某個數去消其它行的數,使他們第i列變為0 。
具體實現為:用第i行去消第j行,令\(x=a_{j,i}/a_{i,i}\),對於第j行的第k個數使\(a_{j,k}-=a_{i,k}*x\)
這樣,消元后的結果用矩陣表示出來就是一個階梯形的矩陣,第i行的元素有\(a_{i,i}\)~ \(a_{i,n}\)。
\(step 2\)
現在我們最後一項只有一個未知數,所以我們從後往前列舉,每次求出一個未知數後,把該值分別代入前面的方程,再解前面一個方程,就能求出所有未知數。
在消元完成之後,一定為三種情況之一:
1.若存在係數全部為 0 但常數不為 0的項,方程組無解。
2.若係數不為 0 的行恰好有 n 個,說明方程恰好有 1個解。
3.若係數不為 0 的行有 k<n 個,說明主元有 k 個,自由元有 n−k 個,方程有無數多個解。
#include<bits/stdc++.h> using namespace std; const int M=2005; double a[M][M],ans[M],EPS=1e-7; bool Free[M]; int n,m,rk; bool Gauss(){ int i,j; for(i=0,j=0;i<n&&j<m;i++,j++){ int mx=i; for(int k=i+1;k<n;k++){ if(fabs(a[k][j])>fabs(a[mx][j])) mx=k; } if(fabs(a[mx][j])<EPS){ printf("No Solution"); return 1; } if(mx!=i){ for(int k=j;k<=m;k++){ swap(a[i][k],a[mx][k]); } } for(int k=0;k<n;k++){ if(k!=i&&fabs(a[k][j])>EPS){ double x=a[k][j]/a[i][j]; for(int u=m;u>=j;u--){ a[k][u]-=x*a[i][u]; } } } } rk=i; return 0; } int main(){ scanf("%d",&n); m=n; for(int i=0;i<n;i++){ for(int j=0;j<=n;j++){ scanf("%lf",&a[i][j]); } } if(Gauss()){ return 0; } for(int i=rk;i<n;i++){ if(fabs(a[i][m])>EPS){ printf("No Solution"); return 0; } } if(rk<m){ printf("No Solution"); return 0; } for(int i=0;i<m;i++){ Free[i]=1; } for(int i=rk-1;i>=0;i--){ int cnt=0,pos; for(int j=0;j<m;j++){ if(fabs(a[i][j])>EPS&&Free[j]){ cnt++;pos=j; } } if(cnt==1){ Free[pos]=0; ans[pos]=a[i][m]*1.0/a[i][pos]; } } for(int i=0;i<n;i++){ printf("%.2f\n", fabs(ans[i])<EPS?0:ans[i]); } }
P4035 [JSOI2008]球形空間產生器
題目給了我們n+1個n元二次方程,無法直接使用高斯消元。
於是我們用第一個方程去減剩下的n個,得到了n個n元一次方程,直接套用高斯消元即可
帶狀矩陣
當求某些特殊問題(如概率dp,期望dp),矩陣係數矩陣為帶狀矩陣
例如:
我們每次沿對角線對一個\(d*d\) 的矩陣進行消元:
消完後還剩下:
然後回代即可。
所以消元為 \(O(nd^2)\),回代為\(O(nd)\)。
總複雜度為 \(O(nd^2)\),d為頻寬。
圖片來源lsk學長