求助高斯消元
查看原帖
求助高斯消元
151601
Rusalka楼主2020/8/10 20:05

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

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

2020/8/10 20:05
加载中...