矩阵的应用
一.矩阵的基本运算
加减法:直接进行加减法即可
乘法:对每一行和每一列取乘积和
转置:将矩阵倒置并逆时针旋转90度
二.矩阵的初等变换
1.两行(列)互换
2.某行(列)乘上非零系数
3.某行(列)乘上常数后加到另一行(列)上
三.经典应用
1.求A到B,恰好经过k个点的路径数量
代码:
#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的结果
代码:
#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;
}
技巧:通过矩阵乘法,巧妙地将点的一些操作变化为矩阵的运算

浙公网安备 33010602011771号