高斯消元

高斯消元法用于解决 n 元一次加法方程组、异或方程组、同余方程组。

加法方程组

高斯消元法解决加法方程本质上是对我们小学学过的消元法一样。

需要用到矩阵初等行变换:

  • 矩阵中可以任意交换两行的数值。
  • 矩阵中可以对任意一行的每一列的值进行相同的初等运算 + - * / %。

其实这两个变换对应的操作就是确定主元(防溢出),以及消元过程。

高斯消元法解决加法方程组的过程:

  1. 将 n 元一次方程组填入矩阵,例如:\(x_1a_1 + x_2a_2 + x_3a_3 + ...+ x_na_n = c_1\)填入矩阵的第一行为\([x_1, x_2, x_3, ..., x_n, c_1]\)
  2. 确定主元(选取最大的防溢出),交换到第 i 行,注意要从第 0 行一直找到最后一行,因为前面可能出现自由元。
  3. 将主元所在行的所有元素同除主元。
  4. 利用主元所在行去消除其他行,注意不要把主元所在行消掉。
  5. 重复 2 - 4 步骤 n 次,直到矩阵变为单位矩阵(这个单位矩阵不包括最后一列)。
  6. 判断解的情况,下面记主元为x,最后一列为c,如果出现x = 1 -> c != 0,x即为所求; x = 0 -> c = 0,答案有无穷解;x = 0 -> c != 0,答案为无解。

高斯消元解决加法方程组
code:

#include <iostream>
#include <cmath>

using namespace std;

const int N = 110;
const double eps = 1e-7;
int n;
double a[N][N];
bool zero(double x)
{
    return fabs(x) < eps;
}

int guass()
{
    for(int i = 1; i <= n; i++)
    {
        // 确定主元
        int aim = i;
        for(int j = 1; j <= n; j++)
        {
            if(j < i && !zero(a[j][j])) continue;
            if(fabs(a[j][i]) > fabs(a[aim][i])) aim = j;
        }
        // 判断是否为自由元
        if(zero(a[aim][i])) return 0;

        // 交换aim 和 i 行
        for(int j = 1; j <= n + 1; j++) swap(a[aim][j], a[i][j]);

        // 把第i行变为1
        for(int j = n + 1; j >= i; j--) a[i][j] /= a[i][i];

        // 消元
        for(int j = 1; j <= n; j++)
        {
            if(i == j) continue;
            double t = a[j][i];
            for(int k = i; k <= n + 1; k++) a[j][k] -= a[i][k] * t;
        }
    }
    return 1;
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= n + 1; j++)
        {
            cin >> a[i][j];
        }
    }
    int ret = guass();

    if(ret)
    {
        for(int i = 1; i <= n; i++) 
        {
            printf("%.2lf \n", a[i][n + 1]);
        }
    }
    else cout << "No Solution" << endl; 
    return 0;
}

时间复杂度:\(O(n^3)\)

球形空间产生器

题目大意:有一个球形空间产生器能够在 n 维空间中产生一个坚硬的球体。现在,你被困在了这个 n 维球体中,你只知道球面上 n+1 个点的坐标,你需要以最快的速度确定这个 n 维球体的球心坐标,以便于摧毁这个球形空间产生器。

【解题】:

  1. 球心:到球面上任意一点距离都相等的点。
  2. 距离:设两个 n 维空间上的点 A,B 的坐标为 (a1,a2,⋯,an),(x1,x2,...,xn),则 A,B 的距离定义为:

\(dist = \sqrt{(a_1 - x_1)^ 2 + (a_2 - x_2)^ 2+ (a_3 - x_3)^2 +...+(a_n - x_n)^2}\)

设第一个点的坐标为(tmp1,tmp2,tmp3,...tmpn),之后每行的坐标为(x1,x2,x3,...xn)

则可以的出每行的方程组:\([2*(x_1 - tmp_1), 2*(x_2 - tmp_2),2*(x_3-tmp_3),...,2*(x_n-tmp_n), \sum_{i=1}^n x^2-\sum_{i=1}^n tmp^2]\)

code:

#include <iostream>
#include <cmath>
using namespace std;
typedef long long LL;
#define endl '\n'
const int N = 15;
const double eps = 1e-7;
int n;
double a[N][N];
double tmp[N];

bool zero(double x)
{
    return fabs(x) < eps;
}

void guass()
{
    for(int i = 1; i <= n; i++)
    {
        int aim = i;
        for(int j = 1; j <= n; j++)
        {
            if(j < i && !zero(a[j][j])) continue;
            if(fabs(a[aim][i]) < fabs(a[j][i])) aim = j;
        }
        
        for(int j = 1; j <= n + 1; j++) swap(a[i][j], a[aim][j]);

        for(int j = n + 1; j >= i; j--) a[i][j] /= a[i][i];

        for(int j = 1; j <= n; j++)
        {
            if(j == i) continue;
            double t = a[j][i] / a[i][i];
            for(int k = i; k <= n + 1; k++) a[j][k] -= a[i][k] * t;
        }
    }
}

void solve() 
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> tmp[i];

    for(int i = 1; i <= n; i++)
    {
        double SquR = 0; 
        for(int j = 1; j <= n; j++)
        {
            double x; cin >> x;
            a[i][j] = 2 * (x - tmp[j]);
            SquR += x * x - tmp[j] * tmp[j];
        }
        a[i][n + 1] = SquR;
    }
    // for(int i = 1; i <= n; i++)
    // {
    //     for(int j = 1; j <= n + 1; j++)
    //     {
    //         cout << a[i][j] << " ";
    //     }
    //     cout << endl;
    // }
    guass();

    for(int i = 1; i <= n; i++) printf("%.3lf ", a[i][n + 1]);

}

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T = 1;
	// cin >> T;
	while(T--)
	{
        solve();
	}
	return 0;
}

posted on 2026-06-29 10:22  我不爱吃汉堡  阅读(2)  评论(0)    收藏  举报

导航