求助高斯消元

P2455 [SDOI2006] 线性方程组

Rusalka @ 2020-08-10 20:05:47

我在做这道题的时候最开始写的是这样的:

inline bool gauss()
{
    for(int i=1;i<=n;i++)
    {
        int r = i;
        for(int j=i+1;j<=n;j++)
            if(check(r, j, i)) // 判断是否需要交换,和本帖的问题无关,check 函数的实现基本同第一篇题解
                r = j;
        if(r != i) swap(a[i], a[r]);
        if(fabs(a[i][i]) < eps) continue;
        for(int j=i+1;j<=n;j++)
        {
            double div = a[j][i]/a[i][i];
            for(int k=i;k<=n+1;k++)
                a[j][k] -= div*a[i][k];
        }
    }
    for(int i=n;i>=1;i--)
    {
        if(a[i][i] < eps) continue;
        for(int j=i+1;j<=n;j++)
            a[i][n+1] -= a[j][n+1]*a[i][j];
        a[i][n+1] /= a[i][i];
    }
    bool no = 0, any = 0; 
    for(int i=1;i<=n;i++)
    {
        if(fabs(a[i][i]) < eps && fabs(a[i][n+1]) > eps) no = 1;
        if(fabs(a[i][i]) < eps && fabs(a[i][n+1]) < eps) any = 1;
    }
    if(no)
    {
        puts("-1");
        return 0;
    }
    if(any)
    {
        puts("0");
        return 0;
    }
    return 1;
}

为了节省空间只保留关键部分。
这份代码只能收获 70pts 的好成绩,有把多解判成无解的,也有把有唯一解判成无解的,还有输出错误解的。。。

所以我尝试了另外一种写法,具体是把上一份代码中的

for(int i=1;i<=n;i++)
{
    int r = i;
    for(int j=i+1;j<=n;j++)
        if(check(r, j, i))
            r = j;
    if(r != i) swap(a[i], a[r]);
    if(fabs(a[i][i]) < eps) continue;
    for(int j=i+1;j<=n;j++)
    {
        double div = a[j][i]/a[i][i];
        for(int k=i;k<=n+1;k++)
            a[j][k] -= div*a[i][k];
    }
}

替换成

for(int i=1;i<=n;i++)
{
    int r = i;
    for(int j=i+1;j<=n;j++)
        if(check(r, j, i))
            r = j;
    if(r != i) swap(a[i], a[r]);
    if(fabs(a[i][i]) < eps) continue;
    double div = a[i][i];
    for(int j=i;j<=n+1;j++)
        a[i][j] /= div;
    for(int j=i+1;j<=n;j++)
    {
        div = a[j][i];
        for(int k=i;k<=n+1;k++)
        {
            a[j][k] -= div*a[i][k];
        }
    }
}

也就是在更改了消成上三角矩阵的时候的代码。
这份代码能收获 90pts 的好成绩,错误的地方是第 9 个测试点,把多解判成无解。

我百思不得其解,于是参考了第一篇题解,发现在计算主对角线的时候的代码有所不同。首先是我原来的写法:

for(int i=n;i>=1;i--)
{
    for(int j=i+1;j<=n;j++)
        a[i][n+1] -= a[j][n+1]*a[i][j];
}

下面这个是第一篇题解中的写法:

for(int i=n;i>=1;i--)
{
    if(a[i][i] == 0) continue;
    for(int j=i-1;j>=1;j--)
        a[j][n+1] -= a[j][i]*a[i][n+1], a[j][i] = 0;

}

换成这样就 AC 了。

我的几次提交精度都是设置为 eps = 1e-16,所以应该可以排除是精度上出了问题。

这几种写法感觉是等价的,为什么会前两次会出现各种各样的错误?

望大佬解答 /kel


by ducati @ 2020-08-10 20:14:19

本蒟蒻看看


by ducati @ 2020-08-10 20:22:31

我太菜了看不出问题,如果有人找出了问题可不可以@我一下


by Rusalka @ 2020-08-10 20:25:45

啊那个。。。计算主对角线时,我原先的代码是有判断 if(a[i][i] == 0) continue; 的,和这个没有关系


by Rusalka @ 2020-08-10 20:26:50

@ducati 好的


by ducati @ 2020-08-10 21:40:40

抛个代码, 如果您加入了代码公开计划应该就能看到吧

@ifndef


|