笔记---高斯消元

AcWing.883.高斯消元解线性方程组
输入一个包含 n 个方程 n 个未知数的线性方程组。

方程组中的系数为实数。

求解这个方程组。

下图为一个包含 m 个方程 n 个未知数的线性方程组示例:
在这里插入图片描述
输入格式
第一行包含整数 n n n

接下来 n n n 行,每行包含 n + 1 n+1 n+1 个实数,表示一个方程的 n n n 个系数以及等号右侧的常数。

输出格式
如果给定线性方程组存在唯一解,则输出共 n n n 行,其中第 i i i 行输出第 i i i 个未知数的解,结果保留两位小数。

注意:本题有 SPJ,当输出结果为 0.00 0.00 0.00 时,输出 − 0.00 -0.00 0.00 也会判对。在数学中,一般没有正零或负零的概念,所以严格来说应当输出 0.00 0.00 0.00,但是考虑到本题作为一道模板题,考察点并不在于此,在此处卡住大多同学的代码没有太大意义,故增加 SPJ,对输出 − 0.00 -0.00 0.00 的代码也予以判对。

如果给定线性方程组存在无数解,则输出 Infinite group solutions

如果给定线性方程组无解,则输出 No solution

数据范围
1 ≤ n ≤ 100 1≤n≤100 1n100,所有输入系数以及常数均保留两位小数,绝对值均不超过 100 100 100

输入样例:

1.00 2.00 -1.00 -6.00
2.00 1.00 -3.00 -9.00
-1.00 -1.00 2.00 7.00

输出样例:

-2.00
3.00

通过高斯消元,来使方程变为上三角形式,从而可以解得方程(大学线性代数)
如果消过之后,是完美的上三角形,那么会得到唯一解
如果中间出现 0 = 0 = 0=(非零)的方程,那么无解
如果中间出现 0 = 0 0 = 0 0=0 的方程,那么就有无穷多解

高斯消元的过程:

枚举每一列:
(1)每次枚举找到绝对值最大的一行
(2)将这一行换到最上面
(3)然后将这一行的第一个数变成1
(4)将下面所有行的当前列消成0。之后继续枚举,但是被换到最上面的那一行就不要动了,直至最后。

#include<iostream>
#include<cmath>
using namespace std;
const int N = 110;
const double eps = 1e-8;//浮点数存储有精度问题,小于一个很小的数就相当于等于0了

int n;
double a[N][N];	//存系数矩阵

int gauss() {
	int c, r;	//c:列,r:行

	for (c = 0, r = 0; c < n; c++) {	//枚举每一列
		int t = r;	//从这一行开始
		for (int i = r; i < n; i++)	//找到当前这一列绝对值最大的一行
			if (fabs(a[i][c]) > fabs(a[t][c]))//fabs():浮点绝对值
				t = i;

		if (fabs(a[t][c]) < eps)  continue;	//如果行绝对值的最大值都是0,那么直接跳
		//因为后续的操作就是为了把其化为0,如果已经为0,那么就不需要进行下面的操作了

		for (int i = c; i <= n; i++)swap(a[t][i], a[r][i]);//把绝对值最大的这行换到最上面

		//要倒着进行,不然第一个数(第c列)变成1,后面除的数都不对了
		for (int i = n; i >= c; i--) a[r][i] /= a[r][c];//把这一行的第一个数变成1(整个方程都需要同步除)

		for (int i = r + 1; i < n; i++) {		//把下面所有行的第一个数消成0
			if (fabs(a[i][c]) > eps) {			//如果不是0再进行操作
				for (int j = n; j >= c; j--)
					a[i][j] -= a[r][j] * a[i][c];//把整个方程减去变成1的那个方程的倍数	
			}
		}
		r++;//可操作的行减少一个
	}
	if (r < n) {	//如果处理过的行小于所有的行,则进行进一步判断
		for (int i = r; i < n; i++) {	//如果没有等于0的行,即(0 = (非零))
			if (fabs(a[i][n]) > eps)return 2;//就是无解
		}
		return 1;	//如果有0 = 0 那么有无穷多解
	}

	for (int i = n - 1; i >= 0; i--)		//从下往上,一步步的把下面x的解往上面带
		for (int j = i + 1; j < n; j++)		//从而求解出所有的x的解
			a[i][n] -= a[i][j] * a[j][n];	//减的就是上几行的x的解,因为是上三角形状
	//减完之后等号右面的值就是这一行的x的解

	return 0;//唯一解
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;

	for (int i = 0; i < n; i++) {	//读入
		for (int j = 0; j < n + 1; j++) {	//因为还有得数,所以到n+1
			cin >> a[i][j];
		}
	}

	int t = gauss();

	//用0表示唯一解,1表示无穷解,2表示无解
	if (t == 0) {
		for (int i = 0; i < n; i++) printf("%.2lf\n", a[i][n]);//到最后,矩阵最右面的系数已经是各个x的值了
	}
	else if (t == 1) puts("Infinite group solutions");
	else puts("No solution");

	return 0;
}

AcWing.884.高斯消元解异或线性方程组
输入一个包含 n n n 个方程 n n n 个未知数的异或线性方程组。

方程组中的系数和常数为 0 0 0 1 1 1,每个未知数的取值也为 0 0 0 1 1 1

求解这个方程组。

异或线性方程组示例如下:

M [ 1 ] [ 1 ] x 1 M[1][1]x_{1} M[1][1]x1 ^ M [ 1 ] [ 2 ] x 2 M[1][2]x_{2} M[1][2]x2 ^ … … ^ M [ 1 ] [ n ] x n = B 1 M[1][n]x_{n} = B_{1} M[1][n]xn=B1
M [ 2 ] [ 1 ] x 1 M[2][1]x_{1} M[2][1]x1 ^ M [ 2 ] [ 2 ] x 2 M[2][2]x_{2} M[2][2]x2 ^ … … ^ M [ 2 ] [ n ] x n = B 2 M[2][n]x_{n} = B_{2} M[2][n]xn=B2
… …
M [ n ] [ 1 ] x 1 M[n][1]x_{1} M[n][1]x1 ^ M [ n ] [ 2 ] x 2 M[n][2]x_{2} M[n][2]x2 ^ … … ^ M [ n ] [ n ] x n = B 3 M[n][n]x_{n} = B_{3} M[n][n]xn=B3
其中 ^ 表示异或( X O R XOR XOR), M [ i ] [ j ] M[i][j] M[i][j] 表示第 i i i 个式子中 x j x_{j} xj 的系数, B i B_{i} Bi 是第 i 个方程右端的常数,取值均为 0 0 0 1 1 1

输入格式
第一行包含整数 n n n

接下来 n n n 行,每行包含 n + 1 n+1 n+1 个整数 0 0 0 1 1 1,表示一个方程的 n n n 个系数以及等号右侧的常数。

输出格式
如果给定线性方程组存在唯一解,则输出共 n n n 行,其中第 i i i 行输出第 i i i 个未知数的解。

如果给定线性方程组存在多组解,则输出 Multiple sets of solutions

如果给定线性方程组无解,则输出 No solution

数据范围
1 ≤ n ≤ 100 1≤n≤100 1n100

输入样例:

3
1 1 0 1
0 1 1 0
1 0 0 1

输出样例:

1
0
0

对于此题同样适用高斯消元

1.将系数矩阵化为上三角形式:(1)枚举列,(2)找到非零行,(3)交换到最上面,(4)用交换后的方程将这一列下面的系数消成0

2.如果是完美的上三角形式,那么就有唯一解。如果不是完美的上三角形式:(1)如果有矛盾(0 = 1),那么就说明无解(2)如果无矛盾,那么就有无穷解

代码:

#include<iostream>
using namespace std;

const int N = 110;

int n;
int a[N][N];

int gauss() {
	int r, c;
	for (r = 0, c = 0; c < n; c++) {//板子
		int t = r;
		for (int i = r; i < n; i++) {//找到非零行
			if (a[i][c]) {	//c是固定的,所以其实是在找这一列下面的系数为1的行
				t = i;	//更新t
				break;
			}
		}

		if (!a[t][c])continue;//如果根本找不到,那就直接跳

		for (int i = c; i <= n; i++) swap(a[t][i],a[r][i]);//换到最高行

		for (int i = r + 1; i < n; i++)	//把下面行消成0
			if (a[i][c])//如果当前已经是0的话就不用操作
				for (int j = c; j <= n; j++)//消的操作
					a[i][j] ^= a[r][j];

		r++;
	}

	if (r < n) {//如果不是一个完美的上三角
		for (int i = r; i < n; i++)//判断有没有矛盾
			if (a[i][n])
				return 2;
		return 1;
	}

	for (int i = n - 1; i >= 0; i--)//从下往上代,求解方程组
		for (int j = i + 1; j < n; j++)
			a[i][n] ^= a[i][j] * a[j][n];
	
	return 0;
}

int main() {
	cin >> n;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n + 1; j++) {
			cin >> a[i][j];
		}
	}

	int res = gauss();

	if (res == 0) {
		for (int i = 0; i < n; i++)cout << a[i][n] << endl;
	}
	else if (res == 1)puts("Multiple sets of solutions");
	else puts("No solution");

	return 0;
}
  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值