高斯消元法 Gauss

高斯消元法可以求线性方程组。本质上是暴力模拟手算的过程。

{ 3 x + 2 y + z = 10 5 x + y + 6 z = 25 2 x + 3 y + 4 z = 20 \left\{\begin{array}{l} 3 x+2 y+z=10 \\ 5 x+y+6 z=25 \\ 2 x+3 y+4 z=20 \end{array}\right.

手算的过程为第一个与第二个方程抵消,第二个与第三个抵消。得到一个元二次方程组在经过类似步骤得到解。其中运用了三个小学性质

  1. 两个方程互换解不变。
  2. 一个方程乘以非零数解不变。
  3. 一方程加上另一个方程,解不变。

至于高斯消元法,先将 n n 元一次方程写成 n × ( n + 1 ) n \times (n + 1) 的矩阵,可以任意交换它的行和左边 n n 列,也将一行加到另一行,将一行乘上非零数。

{ 3 2 1 10 5 1 6 25 2 3 4 20 } \left\{\begin{array}{lll|l} 3 & 2 & 1 & 10 \\ 5 & 1 & 6 & 25 \\ 2 & 3 & 4 & 20 \end{array}\right\}

高斯消元法将矩阵消成前 n n 列对角线上元素为 1 1 其它为 0 0 的单位矩阵。第 n + 1 n + 1 列为方程的解。

{ 1 0 0 1 0 1 0 2 0 0 1 3 } \left\{\begin{array}{lll|l} 1 & 0 & 0 & 1 \\ 0 & 1 & 0 & 2 \\ 0 & 0 & 1 & 3 \end{array}\right\}

{ x = 1 y = 2 z = 3 \left\{\begin{array}{l} x = 1 \\ y = 2 \\ z = 3 \end{array}\right.

从方程的意义上,一个未知数的系数至少有一个不为 0 0 ,那么矩阵上为一列中至少一个元素不为 0 0 否则方程无解,所以先判断无解,并将每一列的非零数所在的行移动到对角线上。

for (int i = 1; i <= n; i++) {
	int p = i;
	while (!a[p][i] && p <= n) p++;
	if (p == n + 1) {
		puts("No Solution"); return 0;
	}
	for (int j = 1; j <= n + 1; j++) swap(a[i][j], a[p][j]);
}

因为要让对角线全为 1 1 所以对每一行除上一个数,让这一行对角线位的元素为 1 1 。这样会出现小数,所以数组的类型是 double

double x = a[i][i];  //a[i][i] 会被更新所以用变量保存
for (int j = 1; j <= n + 1; j++) a[i][j] /= x;

再将前 n n 列其它的元素搞成 0 0 A i , i A_{i,i} 的系数消成了 1 1 要好好利用,将其它行的第 i i 个未知数通过 A i , j A_{i,j} 消到 0 0

#include <bits/stdc++.h>
using namespace std;
const int N = 300, INF = 0x3f3f3f3f;
inline int read() {
	int x = 0, f = 0; char ch = 0;
	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}
double a[N][N];
int main() {
	int n = read();
	for (int i = 1; i <= n; i++) 
		for (int j = 1; j <= n + 1; j++)
			a[i][j] = read();
	for (int i = 1; i <= n; i++) {
		int p = i;
		while (!a[p][i] && p <= n) p++;
		if (p == n + 1) {
			puts("No Solution"); return 0;
		}
		for (int j = 1; j <= n + 1; j++) swap(a[i][j], a[p][j]);
		double x = a[i][i]; 
		for (int j = 1; j <= n + 1; j++) a[i][j] /= x;
		for (int j = 1; j <= n; j++) 
			if (i != j) {
				x = a[j][i];
				for (int k = 1; k <= n + 1; k++) a[j][k] -= a[i][k] * x;
			}
	}
	for (int i = 1; i <= n; i++) printf("%.2lf\n", a[i][n + 1]);
	return 0;
}

可以发现高斯消元法有点暴力的成分,时间复杂度为 O ( n 3 ) O(n^3)

模板题 Gym 100644H 配平化学方程式 CF113D Museum

猜你喜欢

转载自blog.csdn.net/qq_39984146/article/details/105008753