矩阵的应用

一.矩阵的基本运算

加减法:直接进行加减法即可

乘法:对每一行和每一列取乘积和

转置:将矩阵倒置并逆时针旋转90度

 

二.矩阵的初等变换

1.两行(列)互换

2.某行(列)乘上非零系数

3.某行(列)乘上常数后加到另一行(列)上

 

三.经典应用

1.求A到B,恰好经过k个点的路径数量

 

HDU2157

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 39+7,mod = 1e3;
int n,m,T;
struct matrix{ll mat[N][N];};
matrix operator *(const matrix &a,const matrix &b){
	matrix c;
	memset(c.mat,0,sizeof(c.mat));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(int k=1;k<=n;k++){
				c.mat[i][j]=(c.mat[i][j]+a.mat[i][k]*b.mat[k][j])%mod;
			}
		}
	}
	return c;
}
matrix quickPow(matrix a,ll b){
	matrix ans;
	memset(ans.mat,0,sizeof(ans.mat));
	for(int i=1;i<=n;i++)ans.mat[i][i]=1;
	while(b){
		if(b&1)ans=ans*a;
		a=a*a;
		b>>=1;
	}
	return ans;
}
int main(){
	while(cin>>n>>m){
		if(!n&&!m)break;
		matrix a;
		memset(a.mat,0,sizeof(a.mat));
		for(int i=1;i<=m;i++){
			int x,y;cin>>x>>y;
			x++,y++;
			a.mat[x][y]=1;
		}
		cin>>T;
		while(T--){
			int x,y,k;
			cin>>x>>y>>k;
			x++,y++;
			matrix p=quickPow(a,k);
			cout<<p.mat[x][y]<<'\n';
		}
	}	
	return 0;
}

 技巧:巧妙的运用了矩阵乘法的运算,将其转化为计算路径条数的方式

2.给定矩阵A,求A + A^2 + A^3 + … + A^k的结果

POJ3233

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
const int N = 39+7;
int n,mod,k;
struct matrix{ll mat[N][N];}a;
matrix operator *(const matrix &a,const matrix &b){
	matrix c;
	memset(c.mat,0,sizeof(c.mat));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(int k=1;k<=n;k++){
				c.mat[i][j]=(c.mat[i][j]+a.mat[i][k]*b.mat[k][j])%mod;
			}
		}
	}
	return c;
}
matrix operator +(const matrix &a,const matrix &b){
	matrix c;
	memset(c.mat,0,sizeof(c.mat));
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++){
			c.mat[i][j]=(a.mat[i][j]+b.mat[i][j])%mod;
		}
	}
	return c;
}
matrix quickPow(matrix a,ll b){
	matrix ans;
	memset(ans.mat,0,sizeof(ans.mat));
	for(int i=1;i<=n;i++)ans.mat[i][i]=1;
	while(b){
		if(b&1)ans=ans*a;
		a=a*a;
		b>>=1;
	}
	return ans;
}
matrix solve(int k){
	if(k==1)return a;
	if(k%2==1){
		matrix pp=solve(k/2);
		matrix qq=quickPow(a,k); 
		matrix ans=pp+quickPow(a,k/2)*pp+qq;
		return ans;
	}
	matrix pp=solve(k/2);
	
	matrix ans=pp+(quickPow(a,k/2)*pp);
	return ans;
}
int main(){
	cin>>n>>k>>mod;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>a.mat[i][j];
		}
	} 
	matrix p=solve(k);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cout<<p.mat[i][j]<<' ';
		}
		puts("");
	}
	return 0;
}

  

 

技巧:运用分治和矩阵的分配律,在O(logk)的复杂度内求解完问题

 

3.给定n和p,求第n个Fibonacci数mod p的值,n不超过2^63

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 3,mod = 1e9+7;
struct matrix{ll mat[N][N];};
matrix operator *(const matrix &a,const matrix &b){
	matrix c;
	memset(c.mat,0,sizeof(c.mat));
	for(int i=1;i<N;i++){
		for(int j=1;j<N;j++){
			for(int k=1;k<N;k++){
				c.mat[i][j]=(c.mat[i][j]+a.mat[i][k]*b.mat[k][j])%mod;
			}
		}
	}
	return c;
}
matrix quickPow(matrix a,ll b){
	matrix ans;
	memset(ans.mat,0,sizeof(ans.mat));
	for(int i=1;i<N;i++)ans.mat[i][i]=1;
	while(b){
		if(b&1)ans=ans*a;
		a=a*a;
		b>>=1;
	}
	return ans;
}
int main(){
	ll n;cin>>n;
	if(n<=2){
		cout<<1<<'\n';
		return 0;
	}
	matrix a;
	memset(a.mat,0,sizeof(a.mat));
	a.mat[1][1]=a.mat[1][2]=1;
	matrix p;
	memset(p.mat,0,sizeof(p.mat));
	p.mat[1][1]=p.mat[1][2]=p.mat[2][1]=1;
	a=a*quickPow(p,n-2);
	cout<<a.mat[1][1]<<'\n';
	return 0;
}

  

技巧:巧妙地使用矩阵快速幂加速递推,复杂度O(logn)

 

4.给定一个递推式,要求在O(logn)复杂度内求解

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 4,mod = 1e9+7;
struct matrix{ll mat[N][N];};
matrix operator *(const matrix &a,const matrix &b){
	matrix c;
	memset(c.mat,0,sizeof(c.mat));
	for(int i=1;i<N;i++){
		for(int j=1;j<N;j++){
			for(int k=1;k<N;k++){
				c.mat[i][j]=(c.mat[i][j]+a.mat[i][k]*b.mat[k][j])%mod;
			}
		}
	}
	return c;
}
matrix quickPow(matrix a,ll b){
	matrix ans;
	memset(ans.mat,0,sizeof(ans.mat));
	for(int i=1;i<N;i++)ans.mat[i][i]=1;
	while(b){
		if(b&1)ans=ans*a;
		a=a*a;
		b>>=1;
	}
	return ans;
}
int main(){
	int T;cin>>T;
	while(T--){
		int n;cin>>n;
		if(n<=3){
			cout<<1<<'\n';
			continue;
		}
		matrix a;
		memset(a.mat,0,sizeof(a.mat));
		a.mat[1][1]=a.mat[1][2]=a.mat[1][3]=1;
		matrix p;
		memset(p.mat,0,sizeof(p.mat));
		p.mat[1][1]=p.mat[1][3]=p.mat[2][1]=p.mat[3][2]=1;
		a=a*quickPow(p,n-3);
		cout<<a.mat[1][1]<<'\n';
	}
	return 0;
}

  技巧:和斐波那契数列一样,巧妙地使用矩阵快速幂加速递推,复杂度O(logn)

 

5.高斯消元

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e2+39+7;
int n;double a[N][N];
int gauss(){
	for(int i=1;i<=n;i++){
		int r=n+1;
		for(int j=i;j<=n;j++)if(fabs(a[j][i])>fabs(a[(r==n+1?0:r)][i]))r=j;
		if(r==n+1)return -1;
		
//		int r=i;
//		for(int j=i+1;j<=n;j++)if(fabs(a[j][i])>fabs(a[r][i]))r=j;
//		cout<<i<<' '<<r<<'\n';
		
		for(int j=1;j<=n+1;j++)swap(a[i][j],a[r][j]);
		if(fabs(a[i][i])<(1e-7))return 0;
		for(int j=n+1;j>=1;j--)a[i][j]/=a[i][i];
		for(int j=1;j<=n;j++){
			if(i==j)continue;
			double m=a[j][i]/a[i][i];
			for(int k=1;k<=n+1;k++)a[j][k]-=a[i][k]*m;
		}
	}
	return 1;
}
int main(){
	cin>>n;
	for(int i=1;i<=n+1;i++)a[0][i]=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n+1;j++){
			cin>>a[i][j];
		}
	}
	int k=gauss();
	if(k==1)for(int i=1;i<=n;i++)printf("x%d=%.2lf\n",i,a[i][n+1]);
	else if(k==-1)cout<<"0";
	else cout<<"-1";
	return 0;
}

  技巧:通过矩阵的初等变换,通过尝试解来推测出每个未知数,复杂度O(n^3)

 

6.点的变换

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 39+7,mod = 1e3;
int n,m,T;
struct matrix{ll mat[N][N];}a[N];
matrix operator *(const matrix &a,const matrix &b){
	matrix c;
	memset(c.mat,0,sizeof(c.mat));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(int k=1;k<=n;k++){
				c.mat[i][j]=(c.mat[i][j]+a.mat[i][k]*b.mat[k][j])%mod;
			}
		}
	}
	return c;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		int x,y;cin>>x>>y;
		a[i].mat[1][1]=x;
		a[i].mat[1][2]=x;
	}
	for(int i=1;i<=m;i++){
		int op,p,q,x;cin>>op>>x;
		if(op==1){
			cin>>p>>q;
			matrix k;
			memset(k.mat,0,sizeof(k.mat));
			k.mat[1][1]=k.mat[2][2]=k.mat[3][3]=1;
			k.mat[1][3]=p;k.mat[2][3]=q;
			matrix ans=k*a[x];
			cout<<ans.mat[1][1]<<' '<<ans.mat[1][2]<<'\n'; 
		}else if(op==2){
			cin>>p;
			matrix k;
			memset(k.mat,0,sizeof(k.mat));
			k.mat[1][1]=k.mat[2][2]=p;
			k.mat[3][3]=1;
			matrix ans=k*a[x];
			cout<<ans.mat[1][1]<<' '<<ans.mat[1][2]<<'\n'; 
		}else if(op==3){
			matrix k;
			memset(k.mat,0,sizeof(k.mat));
			k.mat[2][2]=-1;
			k.mat[1][1]=k.mat[3][3]=1;
			matrix ans=k*a[x];
			cout<<ans.mat[1][1]<<' '<<ans.mat[1][2]<<'\n'; 
		}else if(op==4){
			matrix k;
			memset(k.mat,0,sizeof(k.mat));
			k.mat[1][1]=-1;
			k.mat[2][2]=k.mat[3][3]=1;
			matrix ans=k*a[x];
			cout<<ans.mat[1][1]<<' '<<ans.mat[1][2]<<'\n'; 
		}
	}
	return 0;
}

  技巧:通过矩阵乘法,巧妙地将点的一些操作变化为矩阵的运算

posted @ 2024-08-29 23:49  天雷小兔  阅读(76)  评论(0)    收藏  举报