复习的时候又翻了翻题解,发现了这个科技,挺NB的。
普通的高斯消元的思路是把一行的某一项系数变为1,然后用这个对未操作的等式的这一项消去。
于是,进行消去后的形式变成了这样(a,b,c,d是常数,x,y,z是未知数):
∣a1xb1yc1z=d10b2yc2z=d200c3z=d3∣\begin{vmatrix}a_1x & b_1y &c_1z = d_1 \\ 0 &b_2y &c_2z = d_2
\\ 0 & 0 & c_3 z = d_3\end{vmatrix}∣∣∣∣∣∣a1x00b1yb2y0c1z=d1c2z=d2c3z=d3∣∣∣∣∣∣
所以还有回带操作,对于精度有些许影响,常数稍大,还不便记忆。
普通高消code:
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
int n;
long double Map[105][105],ans[105];
const double eps=1e-10;
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]);
for(int i=1;i<=n;++i){
int r=i;
for(int j=i+1;j<=n;++j)
if(fabs(Map[r][i])<fabs(Map[j][i]))r=j;//选取系数最大的作为主元,用于减小误差
if(i!=r) swap(Map[r],Map[i]);
if(fabs(Map[i][i])<eps){
puts("No Solution");
return 0;
}//这一元最大的系数为0,无解
double Div=Map[i][i];//把这一元系数变成1
for(int j=i;j<=n+1;++j)Map[i][j]/=Div;
for(int j=i+1;j<=n;++j){
Div=Map[j][i];
for(int k=i;k<=n+1;++k)
Map[j][k]-=Map[i][k]*Div;
}//相减
}
ans[n]=Map[n][n+1];
for(int i=n-1;i>0;--i){
ans[i]=Map[i][n+1];
for(int j=i+1;j<=n;++j)
ans[i]-=Map[i][j]*ans[j];
}//回带
for(int i=1;i<=n;++i)printf("%.2Lf\n",ans[i]);
return 0;
}
而我们主角——的高斯-约旦消元法,就更加简单粗暴,直接把这个矩阵变成了(省略等号):
∣a1x00d10b2y0d200c3zd3∣\begin{vmatrix}a_1x & 0 & 0 & d_1 \\ 0 &b_2y & 0 & d_2
\\ 0 & 0 & c_3 z & d_3\end{vmatrix}∣∣∣∣∣∣a1x000b2y000c3zd1d2d3∣∣∣∣∣∣
然后输出的时候直接除以系数就好了:)
具体的操作也很简单, 唯一的区别是,我们每一次都对所有的行列式一起消去这一元。
code:
#include<bits/stdc++.h>
using namespace std;
int n;
long double a[105][105];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
for(int j=1;j<=n+1;++j)
scanf("%Lf",&a[i][j]);
}
for(int i=1;i<=n;++i){
int p=i;
for(int j=i+1;j<=n;++j)
if(fabs(a[j][i])>fabs(a[p][i]))p=j;
swap(a[i],a[p]);
if(fabs(a[i][i])<1e-15){
puts("No Solution");
return 0;
}
for(int j=1;j<=n;++j)//与普通高消最大的不同。
if(j!=i){
double tmp=a[j][i]/a[i][i];
for(int k=i+1;k<=n+1;++k)
a[j][k]-=a[i][k]*tmp;
}
}
for(int i=1;i<=n;++i){
printf("%.2Lf\n",a[i][n+1]/a[i][i]);
}
return 0;
}
另外:用long double
时记得使用eps,窝有次就锅在这了,然而用double直接判0即可(