1.向量
一个n维向量可以简单的想成n个数排列在一起,例如V=[123]就是一个三维行向量,其中Vi表示向量V的第i个元素
同理,U=[114514]就是一个二维列向量,为了区分向量与标量,"向量V"一般写作V
向量的运算有加,减,点乘(内积)与叉乘(外积),它们都要满足做运算的两个向量维度相等
假设存在两个n维向量a和b,则存在
a+b=[a1+b1a2+b2a3+b3⋯an+bn]
a−b=[a1−b1a2−b2a3−b3⋯an−bn]
a⋅b=∑i=1nai×bi
因为叉积的定义是在三维空间中垂直于两向量的另一向量,所以标准叉积只存在于3维向量的运算中
a×b=[a2×b3−a3×b2a1×b3−a3×b1a1−b2−a2×b1]
2.矩阵
一个n×m的矩阵可以视作n行m列的数排列在一起,例如A2×3=[142536]就是一个2×3的矩阵
对于矩阵An×m,Ai,j(i∈[1,n],j∈[1,m])表示矩阵第i行第j列上的值
对于矩阵An×m和Bi×j,当且仅当(n==iandm==j)时,我们称这两个矩阵同型
对于矩阵An×m,当n==m时,我们称矩阵A为n阶方阵
对于一个方阵En×n,当且仅当其主对角线上值为1其余值为0时,我们称矩阵E是一个n阶单位矩阵En
矩阵的运算有加减,两个矩阵能够运算当且仅当它们同型
对于矩阵An×m和矩阵Bn×m
An×m+Bn×m=Cn×m,Ci,j=Ai,j+Bi,j(i∈[1,n],j∈[1,m])
An×m−Bn×m=Cn×m,Ci,j=Ai,j−Bi,j(i∈[1,n],j∈[1,m])
对于单个矩阵,我们存在运算"转置",An×mT表示An×m转置后的矩阵
Ai,jT=Aj,i(i∈[1,m],j∈[1,n])
矩阵的运算有数乘,将一个矩阵乘上一个实数k
Ai,j=Ai,j×k(i∈[1,n],j∈[1,m])
矩阵的运算有乘法,对于矩阵An×m和Bi×j,当且仅当m==i时我们能够进行运算AB,这时我们称A右乘上B
对于矩阵An×s和Bs×m,记矩阵Cn×m=An×sBs×m
我们将矩阵的每一行视作一个向量,则Ci,j=Ai⋅BjT=∑i=1kAi,k×Bk,j(i∈[1,n],j∈[1,m])
矩阵乘法的性质:已知矩阵A,矩阵B,矩阵C,单位矩阵E和常数k
EA=AE=AA(BC)=(AB)Ck(AB)=(kA)B
注意!矩阵乘法不存在交换律!!!
3.矩阵快速幂与矩阵加速递推
在实数范围内,nk表示将n连乘k次,根据矩阵乘法的限制可知,如果矩阵A也能够连乘,则A是一个方阵
而矩阵快速幂的步骤与实数的快速幂没有区别,只是更改成了矩阵的乘法而已
这里我们要引入一个定义:常系数齐次线性递推
假设存在数列{f},fi表示数列的第i项
fi=a1×fi−1+a2×fi−2+⋯+ak×fi−k
称f满足k次常系数齐次线性递推
例如斐波那契数列fici=fici−1+fici−2就满足2次常系数齐次线性递推
注意,像fi=fi−1+fi−3这样的数列满足3次常系数齐次线性递推,因为它有一项0×fi−2
先想想我们递推求斐波那契数列的时间复杂度,很EZ且暴力的O(n)对吧
我们把斐波那契数列用矩阵表示一下可以得到
[fifi−1]=A×[fi−1fi−2]
容易发现A应该是一个2×2的方阵,式子变为
[fifi−1]=[A1,1A2,1A1,2A2,2]×[fi−1fi−2]
又由矩阵乘法公式得到
{fifi−1=A1,1×fi−1+A1,2×fi−2=A2,1×fi−1+A2,2×fi−2易得A=[1110]
继续展开得
[fifi−1]=[1110]×[fi−1fi−2]=[1110]2×[fi−2fi−3]=⋯=[1110]n−2×[f2f1]
使用矩阵快速幂的情况下就能以O(∑3log(n))的时间内求出答案(∑是A的阶)
这个时候,我们就称A是这个递推式的"矩阵加速矩阵",通过这个方式我们可以快速求解常系数齐次线性递推的第n项
下面是斐波那契数列的矩阵加速递推代码
matrix base( 2,2 );
base.mat[1][1]=base.mat[1][2]=base.mat[2][1]=1;
matrix right( 2,1 );
right.mat[1][1]=2, right.mat[2][1]=1;
if( n <= 2 ) cout<<right[n%2+1][1];
else cout<<( quick_power( base,n-2 )*right ).mat[1][1];
4.矩阵与图论
已知一个邻接矩阵An×n,考虑一下A2的运算过程
Ai,j2=∑k=1nAi,k×Ak,j
一眼丁真Floyd,所以这个时候Ai,j2就代表了恰好经过两条边从i−>j的方案数
再根据乘法原理,那么易得Ai,jk的含义就是恰好经过k条边从i−>j的方案数
例题:[HNOI2002]公交车路线
matrix mp(8, 8);
for( int i=1;i<=8;i++ )
if( i!=5 )
mp.mat[i][i-1]=mp.mat[i][i+1]=1;
mp.mat[1][0]=mp.mat[8][9]=0;
mp.mat[1][8]=mp.mat[8][1]=1;
cout<<quick_power( mp, n ).mat[1][5];
5.矩阵与多元方程
对于一个n元一次的方程组
⎩⎨⎧a1,1x1+a1,2x2+a1,3x3+⋯+a1,nxn=q1a2,1x1+a2,2x2+a2,3x3+⋯+a2,nxn=q2a3,1x1+a3,2x2+a3,3x3+⋯+a3,nxn=q3⋯an,1x1+an,2x2+an,3x3+⋯+an,nxn=qn我们可以将方程组的系数和常数写成一个n×n+1的矩阵的形式,它称为原方程组的增广矩阵
a1,1a2,1a3,1⋯an,1a1,2a2,2a3,2an,2a1,3⋯a1,na2,3⋯a2,na3,3⋯a3,nan,3⋯an,nq1q2q3qn
将一行乘以k,一行减去另一行等操作称为初等矩阵变换,Guass消元的过程就是模拟初等矩阵变换过程,将增广矩阵变换为上三角矩阵的过程
高斯消元最终会得到形如
100⋯011001⋯11⋯11⋯10⋯1q1q2q3qn
的矩阵,这时从最后一个方程开始不断反代就可以得到每个未知量的解
这里我们介绍另一种消元方式:Gauss−Jordan消元
它最终会将整个矩阵化为除第n+1列以外只有主对角线上存在值且为一的形式(简化行阶梯形)
100⋯001000⋯00⋯01⋯00⋯1q1q2q3qn
这样Ai,n+1就是第i个未知数的解,它的具体操作步骤如下
⎩⎨⎧枚举主元 x,依次消去第 1∼N 个未知量找到满足 Ap,x=0 的第 p 行,p⩾x交换第 x 行与第 p 行将交换后的第 x 行乘以 Ap,x1,使 Ap,x=1(归一化处理)用第 x 行消去其他所有行的变元 x注意一下可能出现这些情况:
<1>线性方程C可以由线性方程A和B组合计算得到,这时候我们称C关于A B线性相关,在方程组中添加一个与原有的方程线性相关的方程不会对该方程组产生任何影响
<2>当我们解的某个未知量U的解是,0×U==0时,U可以任意取值且不对原方程组产生影响,这时候我们称变量U与方程组线性无关
<3>当我们消元结束后,如果存在某一行的值全部被消完了,即对于第i行,Ai,i=0时,若Ai,n+1=0,则变量xi可以任意取值,有无数种解,否则此方程组无解
例题:线性方程组
void jordan( int x )
{
int p=x;
while( p <= n && mat[p][x] == 0 ) p++;
if( p == n+1 ) cout<<"No Solution", exit( 0 );
swap( mat[x],mat[p] );
for( int i=n+1;i>=1;i-- ) mat[x][i]/=mat[x][x];
for( int i=1;i<=n;i++ )
{
if( i == x ) continue;
for( int j=n+1;j>=1;j-- )
mat[i][j]-=( mat[x][j]*mat[i][x] );
}
}
for( int i=1;i<=n;i++ ) jordan( i );
cout<<fixed<<setprecision( 2 );
for( int i=1;i<=n;i++ ) cout<<"x"<<i<<"="<<mat[i][n+1]<<"\n";