更新记录

【1】2020.07.30-16:36

  • 1.完善内容

前言

千万不要将行的意义和列的意义搞混!!!

正文

以下称为矩阵的初等行变换

  • 用非零的数乘某一行
  • 交换两行的位置
  • 将其中一行的若干倍加到另一行上

也就是说,高斯消元就是通过初等行变换去求解变化为增广矩阵的线性方程组

例如下面这个方程组:

\(\begin{cases}2x_0+x_1+5x_2+x_3=23\\-x_0+2x_1+8x_2-4x_3=11\\3x_0+x_1-x_2+2x_3=10\\x_0+6x_1+x_2x_3=20\end{cases}\)

我们把它写成增广矩阵的形式就是:

\(\begin{bmatrix}2&1&5&1&23\\-1&2&8&-4&11\\3&1&-1&2&10\\1&6&1&1&20\end{bmatrix}\)

那么如何求解这个方程组呢?

普通的就是进行加减消元,之后将每个解写成 \(kx_n=y\) 的形式
那么高斯消元就是通过将增广矩阵进行初等行变换求解

变换过程:

\(\begin{bmatrix}0&-11&3&-1&-17\\0&8&9&-3&31\\0&-17&-4&-1&-50\\1&6&1&1&20\end{bmatrix}\)

\(\begin{bmatrix}1&6&1&1&20\\0&-11&3&-1&-17\\0&8&9&-3&31\\0&-17&-4&-1&-50\end{bmatrix}\)

\(\begin{bmatrix}1&6&1&1&20\\0&1&-\dfrac{3}{11}&\dfrac{1}{11}&\dfrac{17}{11}\\0&8&9&-3&31\\0&-17&-4&-1&-50\end{bmatrix}\)

\(\begin{bmatrix}1&0&\dfrac{29}{11}&\dfrac{5}{11}&\dfrac{118}{11}\\0&1&-\dfrac{3}{11}&\dfrac{1}{11}&\dfrac{17}{11}\\0&0&\dfrac{123}{11}&-\dfrac{41}{11}&\dfrac{205}{11}\\0&0&-\dfrac{95}{11}&\dfrac{6}{11}&-\dfrac{261}{11}\end{bmatrix}\)

\(\begin{bmatrix}1&0&\dfrac{29}{11}&\dfrac{5}{11}&\dfrac{118}{11}\\0&1&-\dfrac{3}{11}&\dfrac{1}{11}&\dfrac{17}{11}\\0&0&\dfrac{3}{11}&-\dfrac{1}{11}&\dfrac{5}{11}\\0&0&-\dfrac{95}{11}&\dfrac{6}{11}&-\dfrac{261}{11}\end{bmatrix}\)

\(\begin{bmatrix}1&0&0&\dfrac{4}{3}&\dfrac{19}{3}\\0&1&0&0&2\\0&0&1&-\dfrac{1}{3}&\dfrac{5}{3}\\0&0&0&-\dfrac{7}{3}&\dfrac{28}{3}\end{bmatrix}\)

\(\begin{bmatrix}1&0&0&\dfrac{4}{3}&\dfrac{19}{3}\\0&1&0&0&2\\0&0&1&-\dfrac{1}{3}&\dfrac{5}{3}\\0&0&0&1&4\end{bmatrix}\)

\(\begin{bmatrix}1&0&0&0&1\\0&1&0&0&2\\0&0&1&0&3\\0&0&0&1&4\end{bmatrix}\)

解得(对不起我选的例子不太好计算,但是上面的过程我是一步步计算来的QAQ):

\(\begin{cases}x_0=1\\x_1=2\\x_2=3\\x_3=4\end{cases}\)

所以就解得了

那么用来代码来写就是(用题目说明):

[SDOI2006]线性方程组

为啥不用模板呢?因为模板数据太弱了
这道题也是板子,相比于模板加了无解的判断

因为会存在某些毒瘤卡行的数据,所以我们一切小心
不信你可以试试下面这个数据

in:
2
0 2 3
0 0 0
out:0

最外面的循环i控制消元的列数

\(np\)则是选择这一列系数不为\(0\)的一个

因为前\(i\)个都已经进行过消元了嘛,所以一开始我写的是\(np=i\)
但是这就没有想到前\(i\)个会出现无解或无穷解的情况

所以当出现无解或无穷解的情况时,我们操作不变,看下一,用变量\(line\)统计

然后循环\(line+1\to n\)发现系数不为零的就选它,为了方便实现,我们一般取最大值
至于取绝对值的原因是有可能方程组的系数集合里面没有正数

判断系数是否为零,不是就让这一行回到该到的位置

后面就全是初等行变换进行加减消元了

循环出来之后,看看这个\(line\)是否小于等于\(n\)

如果是,就说明了存在系数为0的情况
必然是无解或者无穷解

循环看看\(n+1\),也就是等式的结果是否为零,如果不是,无解
没有无解就肯定是无穷解啦

最后输出来个判断,防止出现\(-0.00\)的情况

#include<iostream>
#include<cstdio>
using namespace std;
int n,np,line=1,y;
double c,eps=1e-16;
struct matrix{
	double ele[10010];
}m[10010];
inline double abs(double a) {return a>0?a:-a;}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		for(int o=1;o<=n+1;o++)
			scanf("%lf",&m[i].ele[o]);
	for(int i=1;i<=n;i++){
		np=line;
		for(int o=line+1;o<=n;o++)
			if(abs(m[o].ele[i])>abs(m[np].ele[i]))
				np=o;
		if(!m[np].ele[i]) continue;
		swap(m[i],m[np]);
		for(int o=1;o<=n;o++){
			if(o!=line&&m[o].ele[i]){
				c=m[o].ele[i]/m[i].ele[i];
				for(int p=i+1;p<=n+1;p++){
					m[o].ele[p]-=c*m[i].ele[p];
				}
			}
		}
		line+=1;
	}
	if(line<=n){
		for(int i=line;i<=n;i++){
			if(m[line].ele[n+1]){
				printf("-1\n");
				return 0;
			}
		}
		printf("0\n");
		return 0;
	}
	for(int i=1;i<=n;i++){
		printf("x%d=%.2lf\n",i,abs(m[i].ele[n+1]/m[i].ele[i])<eps?0:m[i].ele[n+1]/m[i].ele[i]);
	}
}

[JSOI2008]球形空间产生器

比那动态规划好写多了

看题就能发现,这就是高斯消元的裸题
难点在于如何写出增广矩阵

设每个点为 \(a_1,a_2,\dots,a_{n+1}\)
每个点的坐标为(以 \(a_1\) 为例) \(a_{1,1},a_{1,2},\dots,a_{1,n}\)

球心的坐标为 \(b_1,b_2,\dots ,b_n\)

分析题目,每个点到球心的距离相等,也就是说:
\(\sum\limits_{i=1}^n (a_{1,i}-b_i)^2=\sum\limits_{i=1}^n (a_{2,i}-b_i)^2=\dots =\sum\limits_{i=1}^n (a_{n+1,i}-b_i)^2\)

我们将每个等号两边的式子作差,就会得到\(k=n\)个等式:
\(\sum\limits_{i=1}^n (a_{k,i}-b_i)^2-(a_{k+1,i}-b_i)^2=0\)

\(\sum\limits_{i=1}^n [a_{k,i}^2-a_{k+1,i}^2-2b_i(a_{k,i}-a_{k+1,i})]=0\)

因为只有 \(b_i\) 是未知数,所以我们化为 \(kb_i=y\) 的形式:

\(\sum\limits_{i=1}^n 2b_i(a_{k,i}-a_{k+1,i})=\sum\limits_{i=1}^n (a_{k,i}^2-a_{k+1,i}^2)\)

清晰明了,所以增广矩阵为:

\(\begin{bmatrix}2(a_{1,1}-a_{2,1})&2(a_{1,2}-a_{2,2})&\cdots&2(a_{1,n}-a_{2,n})&\sum\limits_{i=1}^n (a_{1,i}^2-a_{2,i}^2)\\2(a_{2,1}-a_{3,1})&2(a_{2,2}-a_{3,2})&\cdots&2(a_{2,n}-a_{3,n})&\sum\limits_{i=1}^n (a_{2,i}^2-a_{3,i}^2)\\\vdots&\vdots&\ddots&\vdots&\vdots\\2(a_{n,1}-a_{n+1,1})&2(a_{n,2}-a_{n+1,2})&\cdots&2(a_{n,n}-a_{n+1,n})&\sum\limits_{i=1}^n (a_{n,i}^2-a_{n+1,i}^2)\end{bmatrix}\)

然后用及不严谨的高斯消元解就彳亍

#include<iostream>
#include<cstdio>
using namespace std;
int n,np;
double c;
struct matrix{
	double bg[110];
	double ele[110];
}m[110];
int main(){
	cin>>n;
	for(int i=1;i<=n+1;i++)
		for(int o=1;o<=n;o++)
			scanf("%lf",&m[i].bg[o]);
	for(int i=1;i<=n;i++){
		for(int o=1;o<=n;o++){
			m[i].ele[o]=2*(m[i].bg[o]-m[i+1].bg[o]);
			m[i].ele[n+1]+=m[i].bg[o]*m[i].bg[o]-m[i+1].bg[o]*m[i+1].bg[o];
		}
	}
	for(int i=1;i<=n;i++){
		np=i;
		for(int o=i+1;o<=n;o++){
			if(m[o].ele[i]>m[np].ele[i]){
				np=i;
			}
		}
		if(i!=np) swap(m[i],m[np]);
		for(int o=1;o<=n;o++){
			if(o!=i&&m[o].ele[i]){
				c=m[o].ele[i]/m[i].ele[i];
				for(int p=i;p<=n+1;p++){
					m[o].ele[p]-=c*m[i].ele[p];
				}
			}
		}
	}
	for(int i=1;i<=n;i++){
		printf("%.3lf ",m[i].ele[n+1]/m[i].ele[i]);
	}
}

相关文章: