高斯消元
💡 核心思想
高斯消元是求解线性方程组的标准算法。通过初等行变换将增广矩阵化为上三角矩阵(消元),然后从下往上回代求解。在模 2 意义下(异或方程组),高斯消元可以解决开关问题、灯灭问题等组合问题。
高斯消元是线性代数在竞赛中的直接应用。很多选手觉得它太"数学",其实代码非常机械:选主元、消元、回代,三步搞定。在信息学竞赛中,高斯消元最常见的应用是异或方程组(模 2),因为位运算快且好实现。
🎯 直觉理解
想象你在解一个谜题,每个开关控制多个灯。高斯消元就像"逐个击破":先处理第一个灯的开关关系,把其他方程中的第一个灯消掉;然后处理第二个灯,依此类推。最后从下往上回代,就像拼图的最后几块,一个一个放回去。
📝 算法流程
- 写出增广矩阵(n 行 n+1 列)
- 对于每一列 col(从左到右):
- 在第 col 行及以下找绝对值最大的主元行,交换到第 col 行
- 用主元行消去下方所有行的第 col 列元素
- 回代:从最后一行开始,逐个求解变量
- 如果某行系数全为 0 但常数不为 0,则无解;如果全为 0,则有无穷多解
$$a_{ij} = a_{ij} - \frac{a_{ic}}{a_{cc}} \cdot a_{cj} \quad (i > c, \text{ 所有 }j)$$
📊 复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间 | $O(n^3)$ |
| 空间 | $O(n^2)$ |
💻 参考实现(C++)
C++ (C++17)
#include
using namespace std;
const double EPS = 1e-9;
// 高斯消元求解线性方程组 Ax = b
vector gauss(vector> a) {
int n = a.size();
for (int col = 0, row = 0; col < n && row < n; col++, row++) {
// 找主元
int sel = row;
for (int i = row; i < n; i++)
if (abs(a[i][col]) > abs(a[sel][col])) sel = i;
if (abs(a[sel][col]) < EPS) { row--; continue; }
swap(a[sel], a[row]);
// 消元
for (int i = row + 1; i < n; i++) {
double c = a[i][col] / a[row][col];
for (int j = col; j <= n; j++) a[i][j] -= c * a[row][j];
}
}
// 回代
vector ans(n);
for (int i = n - 1; i >= 0; i--) {
double sum = 0;
for (int j = i + 1; j < n; j++) sum += ans[j] * a[i][j];
if (abs(a[i][i]) < EPS) continue; // 无穷多解或无解
ans[i] = (a[i][n] - sum) / a[i][i];
}
return ans;
}
int main() {
vector> a = {
{2, 1, -1, 8},
{-3, -1, 2, -11},
{-2, 1, 2, -3}
};
auto ans = gauss(a);
for (double x : ans) cout << x << " "; // 2 3 -1
cout << endl;
return 0;
} ⚠️ 常见坑点
主元为 0——需要选主元或特判
精度问题——浮点数比较用 EPS
回代时下标越界——注意矩阵是 n×(n+1)
异或方程组——所有运算换成异或和与
📚 相关题目
| 题目 | 来源 | 难度 | 备注 |
|---|---|---|---|
| P3389 高斯消元法 | 洛谷 | CSP-S | 高斯消元模板题 |
| P2447 灯 | 洛谷 | CSP-S | 异或方程组 + 高斯消元 |
| P4035 球形空间产生器 | 洛谷 | CSP-S | 高斯消元 + 几何 |