69.Acwing基础课第883题-简单-高斯消元解线性方程组
69.Acwing基础课第883题-简单-高斯消元解线性方程组
题目描述
\(输入一个包含 n 个方程 n 个未知数的线性方程组\)。
\(方程组中的系数为实数\)。
\(求解这个方程组\)。
\(下图为一个包含 m 个方程 n 个未知数的线性方程组示例:\)
\[ \begin{aligned} a_{11}x_1 + a_{12}x_2 + \dots + a_{1n}x_n &= b_1, \\ a_{21}x_1 + a_{22}x_2 + \dots + a_{2n}x_n &= b_2, \\ \cdots\cdots\cdots\cdots\cdots\cdots\cdots\cdots& \\ a_{m1}x_1 + a_{m2}x_2 + \dots + a_{mn}x_n &= b_m; \end{aligned}
\]
输入格式
\(第一行包含整数 n\)。
\(接下来 n 行,每行包含 n+1 个实数,表示一个方程的 n 个系数以及等号右侧的常数\)。
输出格式
如果给定线性方程组存在唯一解,则输出共 n 行,其中第 i 行输出第 i 个未知数的解,结果保留两位小数。
注意:本题有 SPJ,当输出结果为 0.00 时,输出 -0.00 也会判对。在数学中,一般没有正零或负零的概念,所以严格来说应当输出 0.00,但是考虑到本题作为一道模板题,考察点并不在于此,在此处卡住大多同学的代码没有太大意义,故增加 SPJ,对输出 -0.00 的代码也予以判对。
如果给定线性方程组存在无数解,则输出 Infinite group solutions。
如果给定线性方程组无解,则输出 No solution。
数据范围
1≤n≤100,
所有输入系数以及常数均保留两位小数,绝对值均不超过 100
输入样例:
3
1.00 2.00 -1.00 -6.00
2.00 1.00 -3.00 -9.00
-1.00 -1.00 2.00 7.00
输出样例:
1.00
-2.00
3.00
代码:
// 包含基础输入输出、字符串操作、算法、数学函数头文件
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
// 常量定义:
// N=110:最多支持100个未知数的线性方程组(留冗余)
// eps=1e-8:浮点数精度阈值(判断是否为0,避免浮点误差)
const int N = 110;
const double eps = 1e-8;
int n; // n:方程组的未知数个数(也是方程个数)
double a[N][N]; // 增广矩阵:a[i][j]表示第i行第j列,最后一列a[i][n]是常数项b_i
// 高斯消元核心函数:将增广矩阵化为行最简形,求解线性方程组
// 返回值:0-有唯一解 | 1-有无穷多解 | 2-无解
// 最终解(若有唯一解)存储在a[i][n]中(第i个未知数的解)
int gauss()
{
int c, r; // c:当前处理的列(未知数);r:当前处理的行(方程)
// 遍历每一列(逐个消去未知数)
for (c = 0, r = 0; c < n; c ++ )
{
// ========== 步骤1:找当前列绝对值最大的行(选主元,避免除数过小导致精度误差) ==========
int t = r; // 初始假设当前行是主元行
for (int i = r; i < n; i ++ )
if (fabs(a[i][c]) > fabs(a[t][c]))
t = i; // 更新为主元行(绝对值最大)
// ========== 步骤2:判断当前列是否全为0(该未知数无法消元,跳过) ==========
if (fabs(a[t][c]) < eps) continue;
// ========== 步骤3:将主元行交换到当前处理行(第r行) ==========
for (int i = c; i <= n; i ++ ) swap(a[t][i], a[r][i]);
// ========== 步骤4:将主元行的当前列系数化为1(行变换:当前行 ÷ 主元) ==========
// 从后往前遍历:避免先修改a[r][c]导致后续计算错误
for (int i = n; i >= c; i -- ) a[r][i] /= a[r][c];
// ========== 步骤5:消去当前列下方所有行的系数(化为0) ==========
for (int i = r + 1; i < n; i ++ )
// 若当前行的该列系数非0,才需要消元
if (fabs(a[i][c]) > eps)
// 从后往前消元:避免先修改a[i][c]导致后续计算错误
for (int j = n; j >= c; j -- )
a[i][j] -= a[r][j] * a[i][c];
r ++ ; // 当前行处理完成,下一行待处理
}
// ========== 步骤6:判断解的情况 ==========
// 若处理的行数 < 未知数个数(存在自由变量)
if (r < n)
{
// 检查是否存在 0 = 非0 的矛盾方程(无解)
for (int i = r; i < n; i ++ )
// 若常数项非0,系数全0 → 0 = b_i ≠ 0,无解
if (fabs(a[i][n]) > eps)
return 2;
// 无矛盾方程 → 有无穷多解
return 1;
}
// ========== 步骤7:回代求解(将上三角矩阵化为对角矩阵,得到唯一解) ==========
// 从最后一行往前回代,消去上三角的非对角元素
for (int i = n - 1; i >= 0; i -- )
for (int j = i + 1; j < n; j ++ )
// a[i][n] = a[i][n] - 已求解的未知数 × 对应系数
a[i][n] -= a[i][j] * a[j][n];
return 0; // 有唯一解
}
int main()
{
// 输入未知数个数n
scanf("%d", &n);
// 输入增广矩阵:n行,每行n个系数 + 1个常数项(共n+1列)
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n + 1; j ++ )
scanf("%lf", &a[i][j]);
// 调用高斯消元函数,获取解的情况
int t = gauss();
if (t == 2) puts("No solution"); // 无解
else if (t == 1) puts("Infinite group solutions"); // 无穷多解
else // 有唯一解,输出每个未知数的解(保留2位小数)
{
for (int i = 0; i < n; i ++ )
printf("%.2lf\n", a[i][n]);
}
return 0;
}

浙公网安备 33010602011771号