高斯消元

https://en.wikipedia.org/wiki/Gaussian_elimination
https://en.wikipedia.org/wiki/Invertible_matrix
https://en.wikipedia.org/wiki/Determinant

高斯消元可以做3件事

  1. 解方程
    1. 实数
    2. 整数取模
    3. 异或(模2),bitset优化
  2. 矩阵求逆
  3. 行列式求值

系数矩阵行列式非0(满秩)
那么方程有解且唯一

系数矩阵行列式为0(不满秩)
那么方程要么无解,要么无穷多解

高斯消元

  1. 解方程
  2. 矩阵求逆
  3. 行列式

swap(a, b)
a和b可以是数组,即使数组不可以相互赋值

解方程 3 种情况

  1. 实数
  2. 模质数
  3. 异或方程,用bitset优化

行列式

高斯消元不仅可以用于解方程,还可以用于行列式求值。
但是行列式满足的性质,和方程组略有不同

  1. 交换两行会改变行内列示符号
  2. 为了不改变最终行列式的值,只能是第ii行减去kk倍的第jj行。

实数

性质:
一行所有数字同时乘以k
第i行所有数字都减去第j行对应的数字

需要考虑精度问题,所以在把非零行换到第ii行时,一般换绝对值最大的。

参考代码 Luogu P3389

#include <bits/stdc++.h>
using namespace std;
int n;
double a[120][120];
int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= n; j++) {
            cin >> a[i][j];
        }
        // a[i][0] * x[0] + a[i][1] * x[1] + ... + a[i][n-1] * x[n-1] = a[i][n]
    }
    for (int i = 0; i < n; i++) {
        int t = i;
        for (int j = i; j < n; j++) {
            if (abs(a[t][i]) < abs(a[j][i])) {
                // 换绝对值最大的一行到第i行。
                t = j;
            }
        }
        swap(a[t], a[i]);
        // 不同于赋值,swap可以交换两个数组。
        double u = a[i][i];
        if (fabs(u) < 1e-6) {
            printf("No Solution\n");
            // 这里可能无解,或者无穷多解。
            return 0;
        }
        for (int j = 0; j <= n; j++) {
            a[i][j] /= u;
            // 第i行 /= a[i][i],使得a[i][i] = 1。
        }
        for (int j = 0; j < n; j++) {
            if (i == j) {
                continue;
            }
            // 试图消去除自己之外的所有行。
            u = a[j][i];
            for (int k = 0; k <= n; k++) {
                a[j][k] -= u * a[i][k];
            }
        }
    }
    for (int i = 0; i < n; i++) {
        printf("%.2f\n", a[i][n]);
        // 每一行只有a[i][i] = 1,所以 x[i] = a[i][n]。
    }
    return 0;
}

整数取模

最容易实现的版本,只需要实现逆元即可。

参考代码 Luogu P4783

// 需要O2
#include <bits/stdc++.h>
using namespace std;
int n, a[401][801];
const int p = 1000000007;
// 一定要const int,或者define,否则会超时!!
int pw(int x, int y) {
    int re = 1;
    for (; y > 0; y >>= 1) {
        if (y & 1) {
            re = (long long)re * x % p;
        }
        x = (long long)x * x % p;
    }
    return re;
}
int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> a[i][j];
        }
        a[i][n + i] = 1;
        // 右侧加一个n x n单位矩阵。
    }
    for (int i = 0; i < n; i++) {
        int t = i;
        for (int j = i; j < n; j++) {
            if (a[j][i] > 0) {
            // 换任意非0行即可。
                t = j;
            }
        }
        swap(a[t], a[i]);
        int u = pw(a[i][i], p - 2);
        if (u == 0) {
            printf("No Solution\n");
            return 0;
        }
        for (int j = 0; j < 2 * n; j++) {
            // 变换时,范围是2n
            a[i][j] = (long long)a[i][j] * u % p;
            // u是a[i][i]的逆元。
        }
        for (int j = 0; j < n; j++) {
            if (i == j) {
                continue;
            }
            u = a[j][i];
            for (int k = 0; k < 2 * n; k++) {
                // 变换时,范围是2n
                a[j][k] = (a[j][k] - (long long)u * a[i][k]) % p;
                if (a[j][k] < 0) {
                    a[j][k] += p;
                }
            }
        }
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            printf("%d%c", a[i][j + n], j == n - 1 ? '\n' : ' ');
        }
    }
    // 输出右侧n x n的矩阵。
    return 0;
}

异或版

可以用bitset或者压位来优化常数。

参考代码

参考题目

Luogu P3389
实数 高斯消元,模板题。

bzoj 1013
实数 高斯消元。

Luogu P4783
整数取模 高斯消元。

spoj JZPLIT
spoj JZPLIT2
bitset优化高斯消元解异或方程

https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P4783.cpp
P4783 【模板】矩阵求逆

P3389

https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P3389.cpp
【模板】高斯消元法
高斯消元
枚举第i行:
a[i][i] 可能是 0
找第j行,使得a[j][i] != 0 (对于实数的版本,把绝对值最大的换到第i行可以减少误差)
交换第i行和第j行(必须让a[i][i]不为0)
第i行同时乘以 a[i][i] 的逆元 (除以a[i][i])(让a[i][i]变成1,这里这样写可以让代码更简单
将所有不是第i行的 第j行,减去若干倍的 第i行,使得a[j][i]变成0
(如果 只消去第i行之后的,那么在最后计算答案是,还需要代回求值)

P4035 [JSOI2008]球形空间产生器

x, y
(x-x[1])^2+(y-y[1])^2 == (x-x[2])^2+(y-y[2])^2
- 2 x x[1] + x[1]^2  - 2 y y[1] + y[1]^2 = - 2 x x[2] + x[2]^2  - 2 y y[2] + y[2]^2
相邻2个方程相减,消去二次项

P3389 【模板】高斯消元法
P4783 【模板】矩阵求逆

参考资料

Gaussian elimination

  1. 高斯消元
    1. 行列式
    2. 实数
    3. 整数取模
    4. 异或版
    5. 参考题目
      1. P3389
      2. P4035 [JSOI2008]球形空间产生器
    6. 参考资料