一个 n∗m n ∗ m 矩阵是由 n∗m n ∗ m 个实数排列成n行m列构成的,一般用一对[]
括起来,通常用大写字母表示,比如下面 3∗3 3 ∗ 3 的矩阵简记为 A=(aij)3∗3 A = ( a i j ) 3 ∗ 3
矩阵之间也可以加减,但是矩阵的加减要求是两个大小相同的矩阵,只有当两个矩阵的行列数相同时,才能进行加减,比如一个n行m列的A矩阵和一个n行m列的B矩阵的加减,就是对应位置元素的加减。
A+B=(aij+bij)n∗m A + B = ( a i j + b i j ) n ∗ m
A−B=(aij−bij)n∗m A − B = ( a i j − b i j ) n ∗ m
矩阵的加减满足交换律和结合律
我们定义用实数k右乘矩阵A或者左乘矩阵A,其积等于矩阵A中每个元素乘上k。具体如下
kA=Ak=⎡⎣⎢⎢⎢ka11ka21......kan1ka12ka22......kan2........................ka1mka2m......kanm⎤⎦⎥⎥⎥ k A = A k = [ k a 11 k a 12 . . . . . . k a 1 m k a 21 k a 22 . . . . . . k a 2 m . . . . . . . . . . . . . . . . . . . . . . . . k a n 1 k a n 2 . . . . . . k a n m ]
矩阵A乘上B当且仅当A矩阵的列数和矩阵B行数相同的时候才有定义,即只有 Ant⋅Btm A n t · B t m 才有定义。他们相乘得到一个n行m列的矩阵 Cn∗m C n ∗ m 。C矩阵中每个元素的计算方法为 Cij=ai1b1j+ai2b2j+...+aitbtj=∑tr=1airbrj C i j = a i 1 b 1 j + a i 2 b 2 j + . . . + a i t b t j = ∑ r = 1 t a i r b r j ,即 Cij C i j 等于A的第i行和B的第j列的每个元素对应相乘的和。比如 A=⎡⎣⎢214356⎤⎦⎥ B=[1423] A = [ 2 3 1 5 4 6 ] B = [ 1 2 4 3 ] A⋅B=⎡⎣⎢214356⎤⎦⎥∙[1423]=⎡⎣⎢142128131726⎤⎦⎥ A ⋅ B = [ 2 3 1 5 4 6 ] ∙ [ 1 2 4 3 ] = [ 14 13 21 17 28 26 ]
矩阵的乘法满足结合律但是不满足交换律
const int maxn=101;
const int maxm=101;
struct Matrix{
int n,m;//行 列
int a[maxn][maxm];
void clear(){
n=m=0;
memset(a,0,sizeof(a));
}
Matrix operator +(const Matrix &b) const{
Matrix tmp;
tmp.n=n; tmp.m=m;
for(int i=0;ifor(int j=0;jreturn tmp;
}
Matrix operator -(const Matrix &b) const{
Matrix tmp;
tmp.n=n; tmp.m=m;
for(int i=0;ifor(int j=0;jreturn tmp;
}
Matrix operator *(const Matrix &b) const{
Matrix tmp;
tmp.clear();
tmp.n=n; tmp.m=b.m;
for(int i=0;ifor(int j=0;jfor(int k=0;kreturn tmp;
}
};
矩阵经常被应用到递推、动态规划的转移当中,有一个二维状态数组 dp[n][m] d p [ n ] [ m ] ,有如下转移方程 dp[i][j]=aj1dp[i−1][1]+aj2dp[i−1][2]+...+ajmdp[i−1][m] d p [ i ] [ j ] = a j 1 d p [ i − 1 ] [ 1 ] + a j 2 d p [ i − 1 ] [ 2 ] + . . . + a j m d p [ i − 1 ] [ m ] 。
实际上我们正好可以用一个矩阵乘法来表示这个过程,A矩阵是一个m*m的矩阵,我们一般叫做状态转移矩阵
第二维都是1,2,…,m,于是可以简记成 dp[i]=A⋅dp[i−1] d p [ i ] = A · d p [ i − 1 ]
通过递推可以得到 dp[n]=An−1dp[1] d p [ n ] = A n − 1 d p [ 1 ] ,即我们通过矩阵的乘法表示了dp的转移。
而对于乘法可以采用二分快速幂的方法,求得 An−1 A n − 1 ,从而计算 dp[n] d p [ n ] 。
形如 dp[n]=An−1dp[1] d p [ n ] = A n − 1 d p [ 1 ] ,如果按照一般递推式来正常得到,时间复杂度是 O(nm2) O ( n m 2 ) 的,n是转移的次数, O(m2) O ( m 2 ) 是一次转移的复杂度。
我们可以先求出 An−1 A n − 1 ,同普通数的快速幂一样,矩阵的快速幂也是二分的思想,并注意取模操作,直接看代码
时间复杂度为 O(m3log(n)) O ( m 3 l o g ( n ) )
int solve(int a[],int b[],int m,int t){
//a是常系数数组 b是初值数组 m是数组大小 t是要求解的项数 从第0项开始 所以共有t+1项
//m为已知递推式的阶数
//输出函数在第t项的值f(t)
//调用矩阵类
Matrix M,F,res;//M是辅助常数矩阵 F是转移矩阵
M.clear(),F.clear(),res.clear();
M.n=M.m=m;
res.n=res.m=m;
F.n=m,F.m=1;
for(int i=0;i1;++i)
M.a[i][i+1]=1;
for(int i=0;i1][i]=a[i];//a[i]先存小项的系数 即递推式最靠末尾的系数
F.a[i][0]=b[i];//b[i]先存小项的值 即f0(通常)
res.a[i][i]=1;//初始化为单位矩阵
}
if(treturn F.a[t][0];
for(t-=m-1;t;t/=2){//t-=m-1为还要转移的次数
if(t&1) res=M*res;
M=M*M;
}
F=res*F;
return F.a[m-1][0];
}
用 fib(n) 表示斐波那契数列的第 n 项,现在要求你求 fib(n)mod m,fib(1)=1,fib(2)=1。
斐波那契的递推式是 f[n]=f[n−1]+f[n−2](n>2) f [ n ] = f [ n − 1 ] + f [ n − 2 ] ( n > 2 )
我们构建转移矩阵A A=[0111] A = [ 0 1 1 1 ]
于是有 [f[i−1]f[i]]=A⋅[f[i−2]f[i−1]]=[0111][f[i−2]f[i−1]] [ f [ i − 1 ] f [ i ] ] = A ⋅ [ f [ i − 2 ] f [ i − 1 ] ] = [ 0 1 1 1 ] [ f [ i − 2 ] f [ i − 1 ] ]
当然矩阵的数的位置可以调换(不唯一),编码时应注意
这样就能将复杂度优化为 O(23lgn) O ( 2 3 l g n )
#include
#include
using namespace std;
const int maxn=101;
const int maxm=101;
int mod;//需要时使用
struct Matrix{
int n,m;//行 列
int a[maxn][maxm];//注意函数内数组大小不能超过500000int
void Clear(){
n=m=0;
memset(a,0,sizeof(a));
}
Matrix operator +(const Matrix &b) const{
Matrix tmp;
tmp.n=n; tmp.m=m;
for(int i=0;ifor(int j=0;jreturn tmp;
}
Matrix operator -(const Matrix &b) const{
Matrix tmp;
tmp.n=n; tmp.m=m;
for(int i=0;ifor(int j=0;jreturn tmp;
}
Matrix operator *(const Matrix &b) const{
Matrix tmp;
tmp.Clear();
tmp.n=n; tmp.m=b.m;
for(int i=0;ifor(int j=0;jfor(int k=0;kreturn tmp;
}
};
int solve(int a[],int b[],int m,int t){
//a是常系数数组 b是初值数组 m是数组大小 t是要求解的项数 从第0项开始 所以共有t+1项
//m为已知递推式的阶数
//输出函数在第t项的值f(t)
//调用矩阵类
Matrix M,F,res;//M是辅助常数矩阵 F是转移矩阵
M.Clear(),F.Clear(),res.Clear();
M.n=M.m=m;
res.n=res.m=m;
F.n=m,F.m=1;
for(int i=0;i1;++i)
M.a[i][i+1]=1;
for(int i=0;i1][i]=a[i];//a[i]先存小项的系数 即递推式最靠末尾的系数
F.a[i][0]=b[i];//b[i]先存小项的值 即f0(通常)
res.a[i][i]=1;//初始化为单位矩阵
}
if(treturn F.a[t][0];
for(t-=m-1;t;t/=2){//t-=m-1为还要转移的次数
if(t&1) res=M*res;
M=M*M;
}
F=res*F;//res为最后的常数矩阵
return F.a[m-1][0];
}
int main()
{
mod=1000000007;
int t;
int a[2],b[2];
a[0]=a[1]=1;
b[0]=0;
b[1]=1;
while(cin>>t&&t!=-1)
{
cout<2,t)<return 0;
}