【洛谷】P3389【模板】高斯消元法

【洛谷】P3389【模板】高斯消元法


洛谷专栏:模板,数学
洛谷模板:P3389 高斯消元法
算法竞赛:数学,线性代数
方程算法:高斯消元法
题目链接:洛谷 P3389
相同题目:洛谷 SDOI2006 线性方程组

题目描述
给定一个线性方程组,对其求解。
{ a 1 , 1 x 1 + a 1 , 2 x 2 + ⋯ + a 1 , n x n = b 1 a 2 , 1 x 1 + a 2 , 2 x 2 + ⋯ + a 2 , n x n = b 2 ⋯ a n , 1 x 1 + a n , 2 x 2 + ⋯ + a n , n x n = b n \begin{cases} a_{1, 1} x_1 + a_{1, 2} x_2 + \cdots + a_{1, n} x_n = b_1 \\ a_{2, 1} x_1 + a_{2, 2} x_2 + \cdots + a_{2, n} x_n = b_2 \\ \cdots \\ a_{n,1} x_1 + a_{n, 2} x_2 + \cdots + a_{n, n} x_n = b_n \end{cases} a1,1x1+a1,2x2++a1,nxn=b1a2,1x1+a2,2x2++a2,nxn=b2an,1x1+an,2x2++an,nxn=bn

输入格式
第一行,一个正整数 n n n
第二至 n + 1 n+1 n+1 行,每行 n + 1 n+1 n+1 个整数,为 a 1 , a 2 , … , a n a_1, a_2, \dots ,a_n a1,a2,,an b b b,代表一组方程。
输出格式
n n n 行,每行一个数,第 i i i 行为 x i x_i xi(四舍五入保留 2 2 2 位小数)。
如果不存在唯一解或无解,在第一行输出 No Solution.

数据范围: 1 ≤ n ≤ 100 , ∣ a i ∣ ≤ 10 4 , ∣ b ∣ ≤ 10 4 1 \leq n \leq 100, \left | a_i \right| \leq {10}^4 , \left |b \right| \leq {10}^4 1n100,ai104,b104。保证数据若有解则所有解均满足 ∣ x i ∣ ≤ 10 3 |x_i|\le 10^3 xi103,且 x i ± 10 − 6 x_i\pm 10^{-6} xi±106 x i x_i xi 四舍五入后的结果相同(即不会因为较小的精度误差导致四舍五入后的结果不同)。
提示说明:若某个 x i x_i xi 的解四舍五入后是 0.00,那么你的程序输出 -0.00 和输出 0.00 都是正确的


高斯消元(解线性方程组)

给定一组线性方程组,求方程组的解。

{ a 1 , 1 x 1 + a 1 , 2 x 2 + ⋯ + a 1 , n x n = b 1 a 2 , 1 x 1 + a 2 , 2 x 2 + ⋯ + a 2 , n x n = b 2 ⋯ a n , 1 x 1 + a n , 2 x 2 + ⋯ + a n , n x n = b n \begin{cases} a_{1, 1} x_1 + a_{1, 2} x_2 + \cdots + a_{1, n} x_n = b_1 \\ a_{2, 1} x_1 + a_{2, 2} x_2 + \cdots + a_{2, n} x_n = b_2 \\ \cdots \\ a_{n,1} x_1 + a_{n, 2} x_2 + \cdots + a_{n, n} x_n = b_n \end{cases} a1,1x1+a1,2x2++a1,nxn=b1a2,1x1+a2,2x2++a2,nxn=b2an,1x1+an,2x2++an,nxn=bn

在做数学题时,解方程组一般使用消元思想,有代入消元和加减消元,那么如果把一组方程组喂给计算机,有没有可靠性较强的固定计算流程来求方程组的解呢,这里我们使用高斯消元法来求解线性方程组。

初等行列变换

  1. 把某一行乘以一个非零的数。
  2. 互相交换某两行。
  3. 把某行的若干倍加减到另一行上去。

以上三种变化使方程组变换前后是等价的。

行列变换最终形式

给定一组方程组,通过上述三种初等行列变换得到如下形式:

{ a 1 , 1 x 1 + a 1 , 2 x 2 + ⋯ + a 1 , n x n = b 1 a 2 , 2 x 2 + ⋯ + a 2 , n x n = b 2 ⋯ a n , n x n = b n \begin{cases} a_{1, 1} x_1 + a_{1, 2} x_2 + \cdots + a_{1, n} x_n = b_1 \\ \hspace{1.3cm} a_{2, 2} x_2 + \cdots + a_{2, n} x_n = b_2 \\ \hspace{2.6cm} \cdots \\ \hspace{3.45cm} a_{n, n} x_n = b_n \end{cases} a1,1x1+a1,2x2++a1,nxn=b1a2,2x2++a2,nxn=b2an,nxn=bn

最终得到一个倒三角形的方程组,这时我们可以知道 x n x_n xn 的解,从下往上推,我们能依次知道 x n − 1 , x n − 2 , ⋯   , x 2 , x 1 x_{n-1},x_{n-2},\cdots,x_2,x_1 xn1,xn2,,x2,x1 的解。

但是给定的方程组通过上述三种变换不一定能得到一个完美的倒三角形的方程组,这意味出现了多组解和无解的情况。

在变换的最后,我们调整方程组形式使其变成完整的倒三角形。上述中的完美倒三角形,即方程组中每个方程有实际存在的意义,但不是所有情况下都是完美倒三角形:若出现 0 = 0 0=0 0=0 的方程,那么代表该组方程组有多组解;若出现 0 = c , c 为非零常数 0=c, c \text{为非零常数} 0=c,c为非零常数,那么代表该组方程组无解

高斯消元法

初始时,每行不固定,循环枚举每一列 c c c

  1. 在未固定的行中找到第一个数绝对值最大的一行,将改行移动到未固定的行中的最上方,并固定该行。
  2. 将该行第一个数化为 1 1 1,同时该行其他数也需要等效变换。
  3. 将改行下面所有行的第 c c c 列(有解情况下即第一个数)化为 0 0 0,同时这些行其他数也需要等效变换。

最后我们就得到了上述的倒三角形方程组,由于高斯消元法所有步骤都依据上述初等行列变换,故正确性显而易见。

其他需要注意的是:如果枚举到某一列时,绝对值最大的第一个数为 0 0 0 时,那么该组方程组要么有多组解要么无解,再判断是否有 0 = c , c 为非零常数 0=c, c \text{为非零常数} 0=c,c为非零常数 的情况即可。

该方法的时间复杂度达到了 O ( n 3 ) \mathcal{O}(n^3) O(n3)

其他说明:

  1. 无论输入为整型还是浮点型,一律按浮点型输入即可。
  2. 由于计算机精度问题,需要设置精度常量 e p s eps eps,若两数差小于 e p s eps eps,则认为两数相等,通常 e p s eps eps 设置为 10 − 8 至 10 − 6 10^{-8} \text{至} 10^{-6} 108106 左右。

AC Code。

#include <bits/stdc++.h>
using namespace std;
const int N = 110;
const double eps = 1e-6;//精度常量
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[t][c])<fabs(a[i][c])) t=i;//找绝对值最大的行
        if (fabs(a[t][c])<eps) continue;//最大也为零便不再处理
        for (int i=0;i<=n;i++) swap(a[t][i],a[r][i]);//交换行--该行可以写成 swap(a[t],a[r])
        for (int i=n;i>=c;i--) a[r][i]/=a[r][c];//第一个数化为 1
        for (int i=r+1;i<n;i++)
            if (fabs(a[i][c])>eps)
                for (int j=n;j>=c;j--)
                    a[i][j]-=a[r][j]*a[i][c];//下面所以行第 c 列化为 0
        r++;
    }
    
    if (r<n)
    {
        for (int i=r;i<n;i++)
            if (fabs(a[i][n])>eps) return 2;//出现 0==!0 情况--无解
        return 1;//否则多组解
    }
    
    for (int i=n-1;i>=0;i--)
        for (int j=i+1;j<n;j++)
            a[i][n]-=a[j][n]*a[i][j];//倒推每个解的值
    return 0;
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n;i++)
        for (int j=0;j<=n;j++)
            scanf("%lf",&a[i][j]);
    int t=gauss();
    if (!t)
        for (int i=0;i<n;i++) printf("%.2f\n",a[i][n]);
    else puts("No Solution");
    return 0;
}

End

感谢观看,如有问题欢迎指出。

更新日志

  1. 2025/8/19 开始书写本篇 优快云 博客,并完稿发布。

本篇博客最早由本人发布于洛谷文章广场,本篇博客对其进行了修改调整与完善丰富。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HAH-HAH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值