题解 P3389 【【模板】高斯消元法】
CodyTheWolf · · 题解
在这里只是给大家分享一个判断不是唯一解的小技巧,当然代码是会贴的,代码内会有部分注释。算法不是很难,看看其他大佬的题解或者博客很快就能学会的。
想要快速阅读可以直接看下面的第二点
进入正题:
全部程序打好以后,可能比较头疼的就是怎么判断到底是不是唯一解了,目前有两个办法:
1.判断是否有某个方程的系数全是0。
其实这个也不是很难打,在方程的系数已经满足对角线(i==j)都为1的时候(也就是消完系数,开始回代求解的时候,即f[i][j]==1,i==j)花个O(n^2)一行行扫看看有没有系数全是0的方程。
但是像本蒟蒻懒到不想打判断怎么办???
对于C++玩家来说(C和pascal等暂时没有试过)我们可以利用C++的一个特性(应该可以这么说吧)
2.判断一个解是不是一个数
看到题目是不是有点晕?其实这个过程很简单也很懒。如果您已经搞懂了高斯消元,你会发现,当一组方程所有的系数为0时,我们在计算的过程中这些0肯定是会被除的(比如说在某列系数化一的时候),这就造成了计算错误:0不能为除数。 但是,C++在除0的时候并不会崩溃报错,而是把这个解换成nan(Not a Number),而这个nan的特性就是:
自己不等于自己
想必各位大佬已经想到怎么做了! 我们只需在正常的消元后,判断储存解的数组(在我的代码里是用方程等于号之后的那个位置作为解的)是否有自己不等于自己的数就可以了!
下面是代码:
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=100;
int n;
double f[maxn+1][maxn+1];//用二维数组存系数矩阵
inline void Gauss()//高斯消元的主函数
{
for(register int i=1;i<=n;i++)//选取一列作为消灭系数的对象
{
for(register int j=i;j<=n;j++)//挨个系数化一
for(register int l=n+1;l>=i;l--)
f[j][l]/=f[j][i];
for(register int j=i+1;j<=n;j++)//挨个选取方程,减去系数(可能不太好理解,请自行手推弄懂高斯消元)
for(register int l=i;l<=n+1;l++)
f[j][l]-=f[i][l];
}
for(register int i=n;i>=1;i--)//直接在系数的那个位置乘上未知数,然后移到方程的等号右边
for(register int j=n;j>=i+1;j--)
f[i][j]*=f[j][n+1],f[i][n+1]-=f[i][j];
}
inline bool judge()//判断函数
{
for(register int i=1;i<=n;i++)//挨个寻找解,如果有某个解自己不等于自己,说明没有唯一解
if(f[i][n+1]!=f[i][n+1])
return false;
return true;
}
int main()
{
scanf("%d",&n);
for(register int i=1;i<=n;i++)
for(register int j=1;j<=n+1;j++)
scanf("%lf",&f[i][j]);//输入矩阵
Gauss();//消元
if(judge())//判断是否有解自己不等于自己
for(register int i=1;i<=n;i++)
printf("%.2f",f[i][n+1]),putchar('\n');
else
printf("No Solution");//有的话就是没有唯一的解了
return 0;
}