高斯消元学习笔记
高斯消元是一种用来求解线性方程组(多元一次方程组)的算法。
假设我们现在需要求解一个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学长

浙公网安备 33010602011771号