# 高斯 - 约旦消元
传送门
给出 n 个方程组成的线性方程组求解。
把矩阵通过每列化成对角阵,流程如下。
- 枚举每一列,从剩余行中找出该列中数字最大的一行,并把找到的行换到当前行(与列下标相同),也就是说找到的这个数字现在应该在
- 通过初等行变换,把该列其他数字全部变成 0。
- 重复至化成对角阵。
code:
#include <cstdio> | |
#include <iostream> | |
#include <cmath> | |
#define rep(i, s, t) for(int i = s; i <= t; i++) | |
using namespace std; | |
const double eps=1e-7; | |
const int MAXN = 105; | |
int n; | |
double a[MAXN][MAXN]; | |
int main() | |
{ | |
#ifdef _WIN32 | |
freopen("in.txt", "r", stdin); | |
#endif | |
scanf("%d", &n); | |
rep (i, 1, n) | |
rep (j, 1, n + 1) | |
scanf("%lf", &a[i][j]); | |
rep (i, 1, n) // 枚举每一列(即接下来要保留 a [i][i],该列其他数字通过初等行变换变成 0) | |
{ | |
int now = i; | |
rep (j, i + 1, n) // 找出剩余行里,该列数字最大的行 | |
if (fabs(a[j][i]) > fabs(a[now][i])) | |
now = j; | |
if (fabs(a[now][i]) < eps) // 全是 0,不是满秩,无唯一解 | |
{ | |
printf("No Solution\n"); | |
return 0; | |
} | |
swap(a[i], a[now]); // 找出的行放到当前要保留的行 | |
rep (j, 1, n) | |
{ | |
if (i == j) continue; | |
double d = a[j][i] / a[i][i]; // 要该列其他行的数字都变成 0 | |
rep (k, i, n + 1) // 初等行变换,非齐次还有一列 | |
a[j][k] -= a[i][k] * d; | |
} | |
} | |
rep (i, 1, n) // 最终得到对角阵,统计答案 | |
printf("%.2lf\n", a[i][n + 1] / a[i][i]); | |
return 0; | |
} |
# 求逆矩阵(取模)
传送门
给出 大小的矩阵,求逆矩阵,结果对 取模。
用这个东西:
用高斯 - 约旦消元直接做,记得求逆元。
code:
#include <cstdio> | |
#include <iostream> | |
#define rep(i,s,t) for(int i=s;i<=t;i++) | |
using namespace std; | |
using ll = long long; | |
const int MAXN = 405; | |
const ll MOD = 1e9 + 7; | |
int n; | |
ll a[MAXN][MAXN], b[MAXN][MAXN]; //a 原矩阵,b 跟 a 并在一起的单位矩阵,写成 a [n][n<<1] 可能方便些 (?) | |
inline ll inv(ll num) { | |
ll ans = 1, bb = MOD - 2; | |
num = (num % MOD + MOD) % MOD; | |
for (; bb; bb >>= 1ll) { | |
if (bb & 1ll) ans = (num * ans) % MOD; | |
num = (num * num) % MOD; | |
} | |
return ans; | |
} | |
int main() | |
{ | |
//freopen("in.txt", "r", stdin); | |
scanf("%d", &n); | |
rep (i, 1, n) | |
rep (j, 1, n) | |
scanf("%lld", &a[i][j]); | |
rep (i, 1, n) | |
b[i][i] = 1; | |
rep (i, 1, n) // 枚举每一列 | |
{ | |
int now = i; | |
rep (j, i + 1, n) // 找出这列最大数字所在行 | |
now = a[j][i] > a[now][i] ? j : now; | |
if (!a[now][i]) // 不是满秩,不可逆 | |
{ | |
printf("No Solution\n"); | |
return 0; | |
} | |
if (now != i) // 找出的行交换至当前行 | |
{ | |
swap(a[i], a[now]); | |
swap(b[i], b[now]); | |
} | |
ll div = inv(a[i][i]); // 行变换,第 j 行第 k 列要减去 matrix [i][k] * (a [j][i] /a [i][i]) | |
rep (j, 1, n) // 枚举每一行进行初等行变换 | |
{ | |
if (i == j) | |
continue; | |
ll d = a[j][i] * div % MOD; | |
// 这里循环开始的值不能写成 i, 矩阵 a 第 i 列之前都清零了可以省略,但矩阵 b 前面的列不能省略 | |
rep (k, 1, n) | |
{ | |
a[j][k] = ((a[j][k] - a[i][k] * d % MOD) + MOD) % MOD; | |
b[j][k] = ((b[j][k] - b[i][k] * d % MOD) + MOD) % MOD; | |
} | |
} | |
rep (j, 1, n) // 最终要把 a 处理成单位矩阵,这里直接处理每行 | |
{ | |
a[i][j] = a[i][j] * div % MOD; | |
b[i][j] = b[i][j] * div % MOD; | |
} | |
} | |
rep (i, 1, n) | |
{ | |
rep (j, 1, n) | |
printf("%lld ", b[i][j]); | |
printf("\n"); | |
} | |
return 0; | |
} |
下辈子一定好好学线代 QAQ