以下是正经的搞死小圆学习笔记!
---
数学课上,小圆遇到了一道六元一次方程组的题。颓废的小圆并不想做这道题,作为代课数学老师你决定搞死...不是,帮小圆解答这道题。请你设计程序解任意n元1次方程组。n<=100
【分析】
高斯消元隆重登场。
让我们先来替小圆模拟一下一个三元一次方程的解法(瞎写的,可能系数略恶心)
```math
3x+2y+4z=19 \ \ \ \ (1)
x-y+z=2 \ \ \ \ \ \ \ \ \ \ \ \ (2)
2x+y-3z=-5 \ \ \ \ \ (3)
```
回想小学高年级学到的解方程方法,我们可以通过联立方程消元,从而变成解一元一次方程的问题。
不妨先以(1)为未知数x的基准:
```math
x+ \frac{2}{3}y+\frac{4}{3}=\frac{19}{3} \ \ \ (1)'
```
对于(2)(3),需要在x与基准相同的情况下进行两式相减。一般来说,我们选择用x系数为1的(1)'来“凑”其他待消项的x的系数。
```math
(-1-\frac{2}{3})y+(1-\frac{3}{4}z)=(2-\frac{19}{3})z
→-\frac{5}{3}y-\frac{1}{3}z=-\frac{13}{3} \ \ \ (2)'
```
如此这般,最终消去y,得到z=1
下一步重要的操作是回代,本质是代入消元。
需要注意的是,在刚刚我们用(1)'消去其他方程中的x之后,(1)'就没有再次使用了,而且(1)'中x的系数为1。
遵循同样的方法,我们发现每个方程留下的最后形式,是用它消去的未知数的系数为1的情况,且该方程中的未知数只有这个数及其之后被消的数。
所以,我们回代时只需要用等号右边减去已求的所有数*它们的系数即可。
由于z是第一个被求出来的数,可以发现,回代需要依照倒序。
*考虑几个特殊情况:
1、解方程过程中发现当前未知数的系数在现在及之后的所有方程都为0——也就是说,这个未知数可以取任何值。输出"No Solution"(不存在唯一解)
即可。
2、我们希望关于未知数i的基准是剩余所有方程中i的系数绝对值最大的一个,为了保证答案的精准度。证明戳→[dalao](https://pks-loving.blog.luogu.org/p3389-mu-ban-gao-si-xiao-yuan-fa)
3、想清楚哪些地方应该用double,哪里应该与eps比较(嘤嘤嘤
最后贴代码~
```
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define MAXN 105
#define eps 1e-7
double map[MAXN][MAXN],ans[MAXN];
//map存方程,ans存未知数的解
int n;
bool work(){
for(int i=1;i<=n;i++){
//以下是挑选绝对值最大的系数的过程
int r=i;
for(int j=i+1;j<=n;j++){
if(fabs(map[j][i])>fabs(map[r][i]))
r=j;
}
//情况1.最大都是0???
if(fabs(map[r][i])<eps)
return false;
//无数组解
if(r!=i)//需要交换
swap(map[i],map[r]);
double tmp=map[i][i];
//以下是消元过程
for(int j=i;j<=n+1;j++){//将基准方程中最前未知数系数化为1
map[i][j]/=tmp;
}
for(int j=i+1;j<=n;j++){//其他方程消元
tmp=map[j][i];
for(int k=i;k<=n+1;k++)
map[j][k]-=tmp*map[i][k];
}
}
//以下是回代过程
ans[n]=map[n][n+1];
for(int i=n-1;i>=1;i--){
ans[i]=map[i][n+1];
for(int j=i+1;j<=n;j++)
ans[i]-=(ans[j]*map[i][j]);
}
return true;
//成功求出所有未知数
}
void print(){
for(int i=1;i<=n;i++)
printf("%.2lf\n",ans[i]);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n+1;j++)
scanf("%lf",&map[i][j]);
if(!work())
cout<<"No Solution"<<endl;
else
print();
return 0;
}
```