首页 > 其他分享 >线性代数

线性代数

时间:2023-08-04 17:12:16浏览次数:88  
标签:cn int luogu 矩阵 线性代数 https 高斯消

线性代数

前言:

最近咕咕咕了好久了,1是因为换了教室,2是因为很多题在做,没时间,3则是因为上了线性代数。

目录

矩阵

在c++中,矩阵可以用二维数组来表示,但是乘法的运算有点不同,要重新定义

矩阵的基本运算

1.加/减/数乘运算:直接一一对应直接运算即可
2.乘法运算:遵循左行右列原则,一一对应相乘在相加得到答案
举个例子,c=ab的话,c_i,j=sum(k) a_i,k * b_k,j
注意点:不具有交换律,但具有结合律,时间在O(n^3)
小技巧:i
j的矩阵和jm的矩阵乘法后得到im的矩阵,所以利用结合律先算小矩阵
时间复杂度将会大幅度降低!
3.转置,A的转置A^T=所有的swap(A_i,j,A_j,i)
4.求逆,B是A的逆当且仅当A*B=I。(矩阵乘法意义下)
5.矩阵快速幂(即重新定义乘法后的快速幂)

特殊矩阵

1.单位矩阵I,满足I的主对角线为1,其余都是0,具有A*I=A的性质,可以手推
2.三角矩阵,也分为上三角和下三角两种:
前者:主对角线为分界线(不包括),左下角都是0。
后者:主对角线为分界线(不包括),右上角全是0。

OI中主要常出现这两种,其他不再赘述

矩阵运算的应用

矩阵加速dp

前提:矩阵快速幂

模板链接:https://www.luogu.com.cn/problem/P3390

I have a 矩阵乘法。
I have a 快速幂。
ah~~矩阵快速幂。

个人建议看题解,像题解那样写结构体,更好做。
ac代码:https://www.luogu.com.cn/record/118986645

注:我最开始没听老师劝告,非要自己写,然后后面改回标程做法历程有点烦人,所以看好题解啊!

加速线性dp

这个举个例子,斐波那契,要你求114514114514项,就算记忆化也没p用了。
考虑矩阵的做法:
如图,发现这个矩阵全是常数没有变量,那就成功了!

为什么这么做呢,因为我这样就可以表示成f1*矩阵的某某次方了,快速幂搞定!

例题1:https://www.luogu.com.cn/problem/P3216
思路:这道题矩阵推理出来发现总是和n长度脱不了干系,但他只有1e18啊,所以大不了
我做18种矩阵,分段乘除即可。
ac代码:https://www.luogu.com.cn/record/118988258

例题2:Arc of dream
思路:推理很麻烦,要尽量把乘除拆开变为加减,最后形成了很多项,推的有点小麻烦。
注:vjudge上直接找吧(这现在用的电脑网速太慢)。

广义矩阵运算

这个要详细总结一下
先给题目来看:
https://www.luogu.com.cn/problem/P6190
分析:
F(i,j,k)表示从i到j使用k次魔法的最短路。
F(i,j,0)就是Floyd。
F(i,j,1)=min_{u,v} F(i,u,0)+F(v,j,0)-w(u,v) 。
F(i,j,k)=min_u F(i,u,k-1)+F(u,j,1) k>2。
可以发现,在一次的基础上,就可以矩阵快速幂了。
你问:这个和矩阵乘法有√8关系啊。
要是我把求和符号改成min,把乘积改为求和,那不就是啦!这时候就是广义矩阵运算了!

矩阵应用的一些总结(主要是思路上)

1.以统计方案数为例,假设当前状态可以由n个点转移过来,并且转移过来的方案数都是1。
那么可以让这个是常数矩阵变为01矩阵,可以转移的为1,否则为0,那么乘上矩阵k次幂就是k次转移的ans。
其实这个0/1矩阵可以看成一种邻接表。
例题:
Quad Tiling(poj) 这一题实际上只有可能6种状态,能到达状态间为1(注意不是双向)
魔法值(https://www.luogu.com.cn/problem/P6569)

2.当你dp柿子写出来了,但是又有困惑,不如尝试矩阵来简化。
注:矩阵也可以用在线段树上哦,每个rt都创建一个矩阵,修改也是修改某个矩阵即可。
例题:Can you answer these queries III(https://www.luogu.com.cn/problem/SP1716)

3.要看出广义矩阵乘法还是很难滴,关键要了解清楚矩阵乘法的原理和写法,然后发散思维。
太难了,怎么发散嘛~~~
这里有常见的模型:
a.加法改乘法,乘法改为异或。
b.加法改为min/max,乘法改为加减。

高斯消元(矩阵基础上)

你可曾结果n元一次方程组?
恭喜你,你已经会一半高斯消元了!

在程序中,对方程的操作常常是:
a.交换两个方程的顺序
b.把一个方程的若干倍加到另外一个方程上
c.把一个方程同时乘或者除若干倍

那么和矩阵有什么关系呢?
首先让我们用矩阵乘法来表示很多很多方程组:如图中(1)

然后呢,很显然操作a就是交换矩阵两行,b,c操作同理也可以用矩阵完成

那么,我们对最终矩阵的期望是什么呢?希望他长成什么b样?
我们当然是期望一个I矩阵啦,那么这样一个元素对应一个答案,多好啊
但是期望有时候并不能成真,因为可能存在无解或者是x_n有无限解的情况呢......
甚至,可能方程数量和未知数的个数都不是相等的,这样君又该如何应对?

这里请上两种矩阵,一种我建议在实数域中使用,另一种在整数域中使用

图例子:

整数域使用(当然也可以实数域):行阶梯式矩阵

矩阵要求:

1.所有非全零行要在全零行之上
2.每一行第一个非零数的位置要严格位于上一行第一个非零数的右侧

达成矩阵的方法:

找主元(这一排之后中找)(可以任意了),换主元(非0),这一列消除这一排下面的非0数为0。
这里因为是整数域,所以用辗转相除法

对应算法:高斯消元。

假设我们已经得到了行阶梯式矩阵
先判断无解:如果一个全部为0的行,其对应的答案不为0,无解
其次判断某些元素是否存在无数解情况:(我个人想的)
我们从最后一行向上找,统计能够统计的x的答案并统计x是否有无限解(如果有,x的答案命令为0)
注:不知道是否有无限:-1,一定无限:1,一定不无限:0,一开始全部为-1,令其为f
假设我们现在统计到h行了
首先我们找到这一行第一个非0的位置,假设其所在的位置为p
由于行阶梯式,其下面的肯定比他“短”所以所有p后面的元素应该都是遍历过的
这时候可能出现后面有f=-1的情况,那么肯定是无限解了,令f=1
然后我们知道,只要对于这个方程x_p后面的x_p+1到x_p+n而言,如果有一个解是无限的,
那么p这里肯定也是无限的(会随之变化),这样就命令其f为1
反之后面全为确定的值,相应x_p也确定的,带入计算x_p并令f为0即可。

代码:

#include<bits/stdc++.h> 
using namespace std;
int a[4005][4005];
int ans[4005];
int notd[4005];

int n,m;
void swp(int st,int x,int y){
	for(int i=st;i<=m+1;i++){
		swap(a[x][i],a[y][i]);
	}
}
void sub(int st,int x,int y){
	int tmp=a[x][st]/a[y][st];
	for(int i=st;i<=m+1;i++){
		a[x][i]-=tmp*a[y][i];
	}
}
int main(){
	scanf("%d%d",&n,&m);
	memset(ans,0,sizeof(ans));
	memset(notd,-1,sizeof(notd));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m+1;j++){
			scanf("%d",&a[i][j]);
		}
	}
	int c=1;//枚举开始的位置 ,因为可能存在一列都为0的情况,但此时又不能跳行,应该是c 
	for(int i=1;i<=m;i++){//现在正在处理的列
		int now=c;
		for(int j=c+1;j<=n;j++){
			if(a[c][i]<0){
				for(int kk=i;kk<=m+1;kk++){
					a[c][kk]*=-1;
				}
			}
			if(a[j][i]<0){
				for(int kk=i;kk<=m+1;kk++){
					a[j][kk]*=-1;
				}
			}
			//首项系数化为正
			if(a[c][i]<a[j][i]){//把大的换到这一行来去 ,这里再放一次是为了保证0的RE问题 
				swp(i,c,j);
			}
			while(a[j][i]){
				if(a[c][i]<a[j][i]){//把大的换到这一行来去 
					swp(i,c,j);
				}
				sub(i,c,j);
				swp(i,c,j);
			}
		}
		if(a[c][i]==0){
			continue;
		}
		
		c++;
	}
//	for(int i=1;i<=n;i++){
//		for(int j=1;j<=m+1;j++){
//			printf("%d ",a[i][j]);
//		}
//		printf("\n");
//	} 
	//这就是普通的高斯消元了,开始回代检验 
	for(int i=1;i<=n;i++){//判断无解 
		int have=0;
		for(int j=1;j<=m;j++){
			if(a[i][j]!=0){
				have++;
			}
		}
		if(have==0&&a[i][m+1]!=0){
			printf("No solution");
			return 0;
		}
	} 
	for(int i=n;i>=1;i--){//回代检验,最后一行开始 
		int an=a[i][m+1];
		int have=0;
		int pos=0;
		bool pan=0;
		for(int j=1;j<=m;j++){
			if(a[i][j]!=0&&pos==0){
				pos=j;
				have++;//寻找主元位置 
			}
		}
		if(pos==0){
			continue;
		}
		for(int j=pos+1;j<=m;j++){
			if(notd[j]==-1){
				notd[j]=1;//到了这一项,后面还不确定的都是无限的 (1)
			}
		}
		for(int j=pos+1;j<=m;j++){
			if(a[i][j]!=0){
				have++;
				if(notd[j]==1){//后面组合体中,有一个无限,则整体无限(1) 
					notd[pos]=1;
					break;
				} 
			}
		}
		if(notd[pos]==1){
			ans[pos]=0;//不会影响后面的,因为唯一值的答案不会涉及到他,很多答案的则是看到notd就输出 
			continue;
		}
		 
		if(have==1){
			ans[pos]=an/a[i][pos];
			notd[pos]=0;//肯定能确定的 
		} 
		else{
			for(int j=pos+1;j<=m;j++){
				an-=ans[j]*a[i][j];
			}
			ans[pos]=(an/a[i][pos]);
			if(notd[pos]==-1){//如果后面全是确定的,那么这个肯定是确定的(0) 
				notd[pos]=0;
			}
		}
	}
	for(int i=1;i<=m;i++){
		printf("X[%d] ",i);
		if(notd[i]==1){
			printf("not determined\n");//无限解的情况
		}
		else{
			printf("= %d\n",ans[i]);
		}
	}
	
} 

实数域使用(整数域用lcm会容易导致爆long long):简化行阶梯

矩阵要求:

1.它是个行阶梯矩阵。
2.每一行第一个非零数必须为1,且在这一列中除了他为1外其余都是0

显然这个应该是高斯消元的高级版,但缺陷可能比高斯多

算法:高斯-若当消元

算法晋升:对于某一行,找主元找abs最大的,
并且要求主元确定后每一行都找和消而不是从下一行开始找和消

算法垃圾处:无法应用辗转相除,只能利用lcm,然后爆long long......

判断无解:同上
判断多解:这一行只有1个1时,这个1对应的就是唯一解,否则都是无限解

代码:



#include<bits/stdc++.h>
using namespace std;
double a[405][405];
double eps=1e-8;
bool ans[405];
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	memset(ans,0,sizeof(ans));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m+1;j++){
			scanf("%lf",&a[i][j]);
			a[i][j]=1.0000*a[i][j];
		}
	}
	int c=1;//枚举开始的位置 ,因为可能存在一列都为0的情况,但此时又不能跳行,应该是c 
	for(int i=1;i<=m;i++){//现在正在处理的列
		int now=c;
		for(int j=c+1;j<=n;j++){//寻找绝对值最大的
			 if(fabs(a[j][i])-fabs(a[now][i])>eps){
			 	now=j;
			 }
		} 
		for(int j=i;j<=m+1;j++){
			swap(a[now][j],a[c][j]);//交换过去 
		}
		if(a[c][i]==0)//最大值等于0则说明该列都为0,肯定无解 
		{
			continue;
		}
		for(int j=i+1;j<=m+1;j++){
			a[c][j]=1.0*a[c][j]/a[c][i];//首项系数化为1 
		}
		a[c][i]=1;
		for(int j=1;j<=n;j++){//开始消除 
			if(j==c){
				continue;
			}
			double tmp=1.0000*a[j][i];//目标对象缩放系数
			for(int k=i;k<=m+1;k++){
				a[j][k]-=1.0000*tmp*a[c][k];
			} 
			
		} 
		c++;
	}
	for(int i=1;i<=n;i++){
//		for(int j=1;j<=m+1;j++){
//			printf("%.4lf ",a[i][j]);
//		}
//		printf("\n");
		bool pan=1;
		for(int j=1;j<=m;j++){
			if(fabs(a[i][j])>eps){
				pan=0;
				break;
			}
		}
		if(fabs(a[i][m+1])>eps&&pan==1){
			printf("No solution");
			return 0;
		}
	}
	for(int i=1;i<=n;i++){
		int have=0;
		for(int j=1;j<=m;j++){
			if(fabs(a[i][j])>eps){
				have++;
			}
		}
		if(have==1){
			for(int j=1;j<=m;j++){
				if(fabs(a[i][j])>eps){
					ans[j]=1;
					break;
				}
			}
		}
	}
	for(int i=1;i<=m;i++){
		printf("X[%d] ",i);
		if(ans[i]==1){
			int to;
			for(int j=1;j<=n;j++){
				if(fabs(a[j][i])==1){
					to=j;
				}
			}
			if(abs(a[to][m+1])<eps){
				a[to][m+1]=0;
			}
			printf("= %.4lf\n",a[to][m+1]);
		} 
		else{
			printf("not determined\n");
		}
	}
	
} 


高斯消元拓展:

个人感觉,其实只要能看出是很多方程组就好了。

1.异或高斯消元

例题1:开关问题(oj上搜吧)http://222.180.160.110:1024/contest/4036/problem/3
ac代码:不好贴过来
例题2:和谐矩阵:https://www.luogu.com.cn/problem/P3164
ac代码:https://www.luogu.com.cn/record/119012132

2.离线给出常数的高斯消元

对于离线给出常数,例如分别为b1,b2,b3....bn,直接把他们接在原来矩阵后跟着算不就完了,不会互相影响
例题:虫食算 https://www.luogu.com.cn/problem/P1092
ac代码:https://www.luogu.com.cn/record/119012885

标签:cn,int,luogu,矩阵,线性代数,https,高斯消
From: https://www.cnblogs.com/linghusama/p/17606493.html

相关文章

  • 【线性代数】求逆矩阵的方法
    1.用公式,将求逆转化为求伴随矩阵和行列式2.根据性质,可逆矩阵一定可以写成一系列初等矩阵乘积的形式3.根据可逆的定义,找到能使AB=E成立的矩阵B(不过这个方法一般适合用于一些简单的或者形式特殊的矩阵。4.通过分块矩阵求逆的性质,将大矩阵的求逆转换为小矩阵求逆。......
  • 线性代数 | 机器学习数学基础
    前言线性代数(linearalgebra)是关于向量空间和线性映射的一个数学分支。它包括对线、面和子空间的研究,同时也涉及到所有的向量空间的一般性质。本文主要介绍机器学习中所用到的线性代数核心基础概念,供读者学习阶段查漏补缺或是快速学习参考。线性代数行列式1.行列式按行(列)展开......
  • 线性代数
    线性代数在高铁上听了听线性代数的课,概念有点多,怕忘了。行列式一些基础的东西N(A):排列\(A\)的逆序数。2阶行列式:\(A=\begin{vmatrix}a&b\\c&d\end{vmatrix}=ad-bc\)3阶行列式:$A=\begin{vmatrix}a&b&c\\d&e&f\\g&j&i\end{vmatrix}=aei+djc+bf......
  • 3b1b 线性代数本质 学习笔记
    导航向量基向量特征向量矩阵行列式逆矩阵矩阵与方程组秩满秩列空间零空间核点积叉积基变换向量空间中的箭头\(\vec{v}\)可以自由移动/一般以原点为起点有序的数字列表\(\begin{bmatrix}a\\\vdots\\b\end{bmatrix}\)相加相乘有意义的东西......
  • 线性代数
    x=torch.arange(4)print(x)print(x[3])print(len(x))print(x.shape)tensor([0,1,2,3])tensor(3)4torch.Size([4])一些简单的属性A=torch.arange(20).reshape(5,4)print(A)print(A.T)tensor([[0,1,2,3],[4,5,6,7],[......
  • 线性代数
    7/19[ABC189E]RotateandFlip题意:给定在二维平面上的一些点,给定\(m\)次操作,为顺时针转$\frac{\pi}{2}$,逆时针旋转$\frac{\pi}{2}$,以某某对称轴对称,每次询问在\(x\)次操作后的某个点的坐标。\(n,m,q\le2\times10^{5}\)由于每个操作是整体操作,所以我们存下整......
  • 线性代数4 初等变换、初等矩阵、分块矩阵、方阵行列式
    1.1初等变换和初等矩阵的概念初等变换的概念:初等变换并不是一个运算操作,而是一类对矩阵的操作的统称对于m×n矩阵A:(1)倍乘:对A的某行或某列元素乘上一个非零常数k(2)互换:互换A的某两列或某两行元素的位置(3)倍加:将A的某行或某列元素的k倍加到另一行或列上这三种变换统称为初等(行......
  • 线性代数基础
    本文内容非常初等。基础知识来不及了,先凑活一下吧。向量向量运算解方程线性代数很大一部分在干的事就是解方程,对于一个方程组,我们可以写成\(Ax=b\)的形式。其中\(A\)是系数矩阵,\(x,b\)是向量。高斯消元线性基......
  • 求线性代数逆序数概念是啥意思?
    想要搞明白线性代数的“逆序”问题,不需要直接看生硬的概念,直接上手做几道题,循序渐进的就明白了——简单的说,只需要看下面这三篇笔记:你知道怎么判断一组数字的逆序数吗?你会使用逆序计算这个行列式吗?利用逆序求\(n\)阶行列式的值​......
  • BZOJ 3996: [TJOI2015]线性代数 最大权闭合子图 最小割
    3996:[TJOI2015]线性代数TimeLimit: 10Sec  MemoryLimit: 128MBSubmit: 1499  Solved: 885[Submit][Status][Discuss]Description给出一个N*N的矩阵B和一......