线性代数
线性代数在 OI 中也有非常多的应用,简单地介绍一下。
矩阵加速
一、例题
显然这样的数有递推式 ,,显然要用矩阵加速优化递推,注意要分段。
注意! 重载运算符的话一定要注意前后顺序!否则的话会出错。
矩阵行列式
一、表示
- 一个 的正方形矩阵 的行列式记为 或 ,一个 矩阵的行列式可表示如下:
- 一个 矩阵的行列式等于其任意行(或列)的元素与对应的代数与余子式乘积之和,即:
二、性质
-
矩阵某两行(列)交换,行列式 。
-
矩阵某一行(列)加上另一行(列),行列式不变。
-
矩阵某一行(列) ,行列式 。
三、代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read() {
ll sum = 0, ff = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') {
ff = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
sum = sum * 10 + ch - 48;
ch = getchar();
}
return sum * ff;
}
void write(ll x) {
if(x < 0)
putchar('-'), x = -x;
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
const int N = 607;
int n, mod, a[N][N], tmp, ans = 1, opt = 1;
void sol() {
for(int i = 1; i <= n; i++)
for(int j = i + 1; j <= n; j++) {
while(a[i][i]) {//辗转相减法
tmp = a[j][i] / a[i][i];
for(int k = i; k <= n; k++)
a[j][k] = (a[j][k] - (ll)a[i][k] * tmp % mod + mod) % mod;
swap(a[i], a[j]), opt = -opt;
}
swap(a[i], a[j]), opt = -opt;
}
for(int i = 1; i <= n; i++)
ans = (ll)ans * a[i][i] % mod;
ans *= opt;
}
int main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
n = read(), mod = read();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
a[i][j] = read();//, a[i][j] %= mod;
sol();
write((ans + mod) % mod);
return 0;
}
高斯消元
一、简介
高斯消元是求解线性方程组、行列式计算等的经典算法,在很多方面都有涉及,因此有必要学一下。
二、增广矩阵
有方程组
写成矩阵的形式为
这就是 增广矩阵,即方程组系数矩阵 与常数列 的并发生的新矩阵,表示为 。
三、操作
从整体上来说,我们从上到下依次处理每一行,处理完第 行后,让 非 ,而 均为 ,最后让 为 ,过程如下:
然后从下往上依次回带,就能得到所有的 值。
-
将增广矩阵中所有 全部变成 ;
根据加减消元法,用第 行的系数将所有 都变成 。具体来说,将 变成 ,第 行按照比例同时变化。然后对于第 行的每一个数 都减去 。相当于每个 减去 。
-
将 变成 ;
这个很简单,第 行的每个数都除以 即可。
-
从下到上回带求出每个 。
四、注意点
-
假设正在处理第 行,则首先要先找一个 且绝对值更大的 ,然后交换第 行和第 行。这样可以减少误差。
-
若当前 ,则高斯消元无法继续,无法确定 ,判断无解。
五、代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read() {
ll sum = 0, ff = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') {
ff = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
sum = sum * 10 + ch - 48;
ch = getchar();
}
return sum * ff;
}
void write(ll x) {
if(x < 0)
putchar('-'), x = -x;
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
const double eps = 1e-8;
const int N = 1e2 + 7;
double tmp, a[N][N], ans[N];
int n, pos;
inline double Abs(double x) {
return x < 0 ? -x : x;
}
inline int cmp(double x, double y) {
if(Abs(x - y) < eps)
return 0;
return Abs(x) > Abs(y) ? 1 : -1;
}
int main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
n = read();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n + 1; j++)
scanf("%lf", &a[i][j]);
for(int i = 1; i <= n; i++) {
//找到 pos 使得 pos > i 且 a[pos][i] 的绝对值 > a[i][i] 的绝对值
pos = i;
for(int j = i + 1; j <= n; j++)
if(cmp(a[j][i], a[pos][i]) == 1)
pos = j;
if(cmp(a[pos][i], 0) == 0)//无解
return puts("No Solution"), 0;
for(int j = 1; j <= n + 1; j++)
swap(a[pos][j], a[i][j]);//交换
//使 a[i][i] = 1
tmp = a[i][i];
for(int j = i; j <= n + 1; j++)
a[i][j] /= tmp;
//使所有 f[j][i] (j > i) = 0
for(int j = i + 1; j <= n; j++) {
tmp = a[j][i];
for(int k = i; k <= n + 1; k++)
a[j][k] -= a[i][k] * tmp;
}
}
//从下到上回带
for(int i = n; i >= 1; i--) {
ans[i] = a[i][n + 1];
for(int j = n; j >= i + 1; j--)
ans[i] -= ans[j] * a[i][j];
}
for(int i = 1; i <= n; i++)
printf("%.2f\n", ans[i]);
return 0;
}
六、例题
用到 高斯-约旦消元法,难点在于判断无解和无数解。
七、参考文献
算法竞赛入门经典 训练指导