高斯消元和线性基

高斯消元

理论

求解线性方程组,例如

\[\begin{cases} a_{1,1}x_1+a_{1,2}x_2+a_{1,3}x_3=b_1\\ a_{2,1}x_1+a_{2,2}x_2+a_{2,3}x_3=b_2\\ a_{3,1}x_1+a_{3,2}x_2+a_{3,3}x_3=b_3 \end{cases} \]

将它转化为矩阵乘法

\[\left[ \begin{matrix} a_{1,1}&a_{1,2}&a_{1,3}\\ a_{1,1}&a_{1,2}&a_{1,3}\\ a_{1,1}&a_{1,2}&a_{1,3} \end{matrix} \right] \times \left[ \begin{matrix} x_1\\ x_2\\ x_3 \end{matrix} \right] = \left[ \begin{matrix} b_1\\ b_2\\ b_3 \end{matrix} \right] \]

上面的三个矩阵还是太麻烦了,我们将他变为增广矩阵

\[\left[ \begin{matrix} a_{1,1}&a_{1,2}&a_{1,3}&b_1\\ a_{1,1}&a_{1,2}&a_{1,3}&b_2\\ a_{1,1}&a_{1,2}&a_{1,3}&b_3 \end{matrix} \right] \]

然后我们定义初等行变换
1.交换两行
2.将同一行的元素同时乘 \(val\)
3.将第 \(i\) 行的元素加到第 \(j\) 行上面
不难发现初等行变换不会改变方程的解。然后就像我们常规解方程一样进行消元就可以了。

具体操作为,对于每一个未知数,我们取一个最大的 \(a\) ,然后用初等行变换将其他行对应的地方变成 \(0\) ,最后我们可以得到一个这样的矩阵

\[\left[ \begin{matrix} a'_1&0&0&b'_1\\ 0&a'_2&0&b'_2\\ 0&0&a'_3&b'_3 \end{matrix} \right] \]

我们就可以很方便的进行求解了。

例题:洛谷P3389

#include <bits/stdc++.h>
#define db double
using namespace std;
db a[105][105];
int n;
int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n+1;j++)
			cin>>a[i][j];
	for(int i=1;i<=n;i++){
		int maxhang=i;
		for(int j=i+1;j<=n;j++)
			if(abs(a[j][i])>=abs(a[maxhang][i]))
				maxhang=j;
		for(int j=1;j<=n+1;j++)
			swap(a[maxhang][j],a[i][j]);
		if(a[i][i]==0) {
			cout<<"No Solution\n";
			return 0;
		}
		for(int j=1;j<=n;j++){
			if(j==i) continue;
			db sb=a[j][i]*1.0/a[i][i];
			for(int k=i;k<=n+1;k++)
				a[j][k]-=a[i][k]*sb;
		}
	}
	for(int i=1;i<=n;i++) {
		cout<<fixed<<setprecision(2)<<a[i][n+1]/a[i][i]<<endl;
	}
	return 0;
}

线性空间

我们先来定义几个概念

向量:一行矩阵或者一列矩阵。线性空间:由一组向量能表示的向量的集合。

线性相关和线性无关:在一组向量中,存在一个向量能够被其他向量线性表示,那么我们称这一组向量是线性相关的。

线性空间的基底:一组线性无关的,数量最多的向量。注意:线性空间的基底不一定唯一

线性空间的维度:一组基底的向量个数。

矩阵的秩:通过高斯消元化成最简阶梯矩阵后,非零行向量/列向量的个数。

例题 P3265 [JLOI2015]装备购买

注意在选最大行的时候按 \(cost\) 选。

异或空间

我们刚刚讨论的是针对加减法的,那异或又有什么性质呢?

异或空间:有一组整数可以通过异或得到的整数集合。

线性相关,线性无关,线性基的定义和上面相似。

那么我们怎么求异或空间的线性基呢?

高斯消元的方法当然是可行的,但是我们有更好的办法。

线性基

考虑一个整数能否对线性基做出贡献,即能够和其中的其他整数异或得到其它位置的 \(1\),那么我们就对于一个要加入的整数 \(x\) 进行如下操作。

令线性基的第 \(i\) 位是 \(bas_i\),那么从高位到低位,如果 \(x\) 的第 \(i\) 位是 \(1\),那么我们考虑 \(bas_i\),若 \(bas_i\) 为零,那么 \(bas_i=x\),否则将 \(x=x\bigoplus bas_i\)

我们可以简单的认为这一系列的操作就是看 \(x\) 能否为线性基贡献一个独一无二的 \(1\)

那么代码实现也很简单了。

例题:P3812 【模板】线性基

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=55;
int n,basis[N];
void Ins(int val){
    for(int i=60;i>=0;i--)
        if(val&(1ll<<i)) {
            if(basis[i]==0) {basis[i]=val; return;}
            else val^=basis[i];
        }
}
signed main(){
    cin>>n;
    for(int i=1,x;i<=n;i++) cin>>x,Ins(x);
    return 0;
}

在这里多提一点,线性基是可以在 \(O(64)\) 的时间内合并的

posted @ 2021-11-21 21:17  Gloamin  阅读(90)  评论(0)    收藏  举报