高斯消元 (gauss elimination) ( g a u s s e l i m i n a t i o n ) 是线性代数中的一个算法。可用来求解线性方程组,并可以求出矩阵的秩,以及求出可逆方阵的逆矩阵。
高斯消元法的原理是:用初等行变换把增广矩阵转换为行阶梯阵,然后回代求出方程的解。
求解一个 n n 元线性方程组。
想一想我们是如何求解二元一次方程组的。
消元,然后带入,对不对?
现在我们就要使用编程模拟这个过程。
方程组为:
a1,1x1+a1,2x2+...+a1,nxn=b1 a 1 , 1 x 1 + a 1 , 2 x 2 + . . . + a 1 , n x n = b 1
a2,1x1+a2,2x2+...+a2,nxn=b2 a 2 , 1 x 1 + a 2 , 2 x 2 + . . . + a 2 , n x n = b 2
... . . .
an,1x1+an,2x2+...+an,nxn=bn a n , 1 x 1 + a n , 2 x 2 + . . . + a n , n x n = b n
每一次找一个使得 |arow,i| | a r o w , i | 最大的 row r o w ,可以有效减小精度误差。
// 高斯消元解 n 元线性方程组
#include
#include
#include
const double eps = 1e-18;
const int maxn = 105;
using namespace std;
typedef double db;
typedef long double ldb;
db t;
int n;
ldb a[maxn][maxn], b[maxn], x[maxn];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%lf", &t);
a[i][j] = t;
}
scanf("%lf", &t);
b[i] = t;
}
for (int i = 1; i <= n; i++) {
int ind = i;
for (int j = i + 1; j <= n; j++) {
if (abs(a[j][i]) > abs(a[ind][i])) {
ind = i;
}
}
if (abs(a[ind][i]) < eps) {
puts("-1");
return 0;
}
if (ind != i) {
swap(b[i], b[ind]);
for (int j = 1; j <= n; j++) {
swap(a[i][j], a[ind][j]);
}
}
for (int j = i + 1; j <= n; j++) {
double d = a[j][i] / a[i][i];
b[j] -= b[i] * d;
for (int k = i; k <= n; k++) {
a[j][k] -= a[i][k] * d;
}
}
/*
for (int j = 1; j <= n; j++) {
for (int k = 1; k <= n; k++) {
printf("%lf ", a[j][k]);
}
printf("%lf\n", b[j]);
}
*/
}
for (int i = n; i > 0; i--) {
for (int j = i + 1; j <= n; j++) {
b[i] -= x[j] * a[i][j];
}
x[i] = b[i] / a[i][i];
}
for (int i = 1; i <= n; i++) {
t = x[i];
printf("x[%d] = %.6lf;\n", i, t);
}
return 0;
}
求 n×n n × n 的矩阵的行列式。
只要将矩阵转化成上三角矩阵即可。
// 高斯消元求矩阵行列式
#include
#include
#include
const int maxn = 105;
const double eps = 1e-6;
typedef long double ldb;
using namespace std;
double t;
int n;
ldb a[maxn][maxn];
double read() {
scanf("%lf", &t);
return t;
}
void write(ldb x) {
t = x;
printf("%lf;\n", t);
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
a[i][j] = read();
}
}
ldb x = 1;
for (int i = 1; i <= n; i++) {
// 为了减小精度误差,取最大的系数
int ind = i;
for (int j = i + 1; j <= n; j++) {
if (abs(a[ind][i]) < abs(a[j][i])) {
ind = j;
}
}
if (ind != i) {
x = -x;
for (int j = 1; j <= n; j++) {
swap(a[i][j], a[ind][j]);
}
}
if (abs(a[i][i]) < eps) {
puts("0");
return 0;
}
for (int j = i + 1; j <= n; j++) {
ldb d = a[j][i] / a[i][i];
for (int k = i; k <= n; k++) {
a[j][k] -= d * a[i][k];
}
}
printf("Step #%d of Gaussian Elimination\n", i);
for (int j = 1; j <= n; j++) {
for (int k = 1; k <= n; k++) {
t = a[j][k];
printf("%lf%c", t, k == n ? '\n' : ' ');
}
}
}
for (int i = 1; i <= n; i++) {
x *= a[i][i];
}
printf("Det(A) = ");
write(x);
return 0;
}
【LightOJ1511】Snakes and Ladders
如果没有蛇,概率动态规划就无后效性,推一下状态转移方程即可。
如果有蛇,我们就视 dp d p 值为未知数,列出一个 n n 元的线性方程组,用高斯消元法来求解即可。
#include
#include
#include
#include
typedef long double ldb;
const ldb eps = 1e-6;
const int maxn = 105;
const int n = 100;
using namespace std;
int tasks, m, to[maxn];
ldb a[maxn][maxn], b[maxn], x[maxn];
int main() {
scanf("%d", &tasks);
for (int task = 1; task <= tasks; task++) {
memset(to, 0, sizeof(to));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
a[i][j] = 0;
}
b[i] = 0, x[i] = 0;
}
scanf("%d", &m);
for (int u, v, i = 1; i <= m; i++) {
scanf("%d %d", &u, &v);
to[u] = v;
}
for (int i = 1; i <= n; i++) {
if (i == n) {
a[i][i] = 1;
} else if (to[i]) {
a[i][i] = 1, a[i][to[i]] = -1;
} else if (i + 6 <= 100) {
for (int j = i + 1; j <= i + 6; j++) {
a[i][j] = -1;
}
a[i][i] = 6, b[i] = 6;
} else {
a[i][i] = 100 - i;
for (int j = i + 1; j <= n; j++) {
a[i][j] = -1;
}
b[i] = 6;
}
}
for (int i = 1; i <= n; i++) {
int ind = i;
for (int j = i + 1; j <= n; j++) {
if (abs(a[j][i]) > abs(a[ind][i])) {
ind = j;
}
}
if (i != ind) {
swap(b[i], b[ind]);
for (int j = 1; j <= n; j++) {
swap(a[i][j], a[ind][j]);
}
}
for (int j = i + 1; j <= n; j++) {
ldb d = a[j][i] / a[i][i];
b[j] -= b[i] * d;
for (int k = 1; k <= n; k++) {
a[j][k] -= a[i][k] * d;
}
}
}
double tmp;
for (int i = n; i >= 1; i--) {
for (int j = i + 1; j <= n; j++) {
b[i] -= a[i][j] * x[j];
}
x[i] = b[i] / a[i][i];
tmp = x[i];
}
tmp = x[1];
printf("Case %d: %.10lf\n", task, tmp);
}
return 0;
}
/*
100
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
*/
其实,这种类型的题目有时也可以用迭代的方法来做,最后往往也能达到题目要求的精度。
碰到一些有后向性的概率动态规划 (n≤500) ( n ≤ 500 ) ,首先就应该想到高斯消元。这是信息学竞赛中的常用手段。