一、题目
二、解法
分条讲一下高斯消元的思路吧,可能不会太严谨,但是尽量通俗易懂吧:
- 枚举我们需要消去的未知数 ,我们选择 去消掉其他行的 ,只留下 ,为避免精度问题,我们需要选择最大的 ( ),然后交换 行(除法中的除数尽量的大)。
- 如果交换后的
几乎等于0
(我们的判断都要基于 (最小进度误差)展开),那么就可以判断为无解或者是无穷多解,我们打上标记,然后跳过 ,继续消元,方便我们最后对于无解和无穷多解的判断。 - 然后就是消元了,消元的方法为枚举要消的行
和列
,那么就可以让
a[j][k]-=a[i][k]*a[j][i]/a[i][i];
- 消完之后判断是否有标记,如果有标记,我们遍历整个矩阵,如果有一行的 系数均为 且等于的值不为 ,那么就可以判断为无解,否则就有无穷多组解。
- 矩阵已经被我们消成了对角线,我们用
a[i][n+1]/a[i][i]
即可算出 的值。
高斯消元被我写成了一个函数,传入要消的矩阵大小和矩阵,在传入存解的数组,即可得到解,返回值是 代表着无穷多解, 代表者无解, 代表着有解,个人觉得讲得很清楚了,看代码(sdoi2006)吧。
#include <cstdio>
#include <iostream>
#define eps 1e-7
#define db double
const int M = 105;
using namespace std;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n;db a[M][M],b[M];
int cmp(db x,db y)
{
if(x-y>eps) return 1;
if(x-y<-eps) return -1;
return 0;
}
bool gauss(int n,db a[][M])
{
bool flag=1;
for(int i=1;i<=n;i++)
{
int Max=i;
for(int r=i+1;r<=n;r++)
if(cmp(a[Max][i],a[r][i])==-1)
Max=r;
swap(a[i],a[Max]);
if(cmp(a[i][i],0)==0)
{
flag=0;
continue;
}
for(int j=1;j<=n;j++)
{
if(i==j || cmp(a[j][i],0)==0) continue;
for(int k=i+1;k<=n+1;k++)
a[j][k]-=a[i][k]*a[j][i]/a[i][i];
a[j][i]=0;
}
}
return flag;
}
int get(int n,db a[][M],db *b)
{
if(!gauss(n,a))
{
for(int i=1;i<=n;i++)
{
bool flag=1;
for(int j=1;j<=n;j++)
if(cmp(a[i][j],0)!=0)
flag=0;
if(flag && cmp(a[i][n+1],0)!=0)
return -1;
}
return 0;
}
for(int i=1;i<=n;i++)
b[i]=a[i][n+1]/a[i][i];
return 1;
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n+1;j++)
scanf("%lf",&a[i][j]);
int g=get(n,a,b);
if(g==-1)
{
puts("-1");
return 0;
}
if(g==0)
{
puts("0");
return 0;
}
for(int i=1;i<=n;i++)
printf("x%d=%.2lf\n",i,b[i]);
}