一个矩阵就是一个二维数组,为了方便声明多个矩阵,我们一般会将矩阵封装一个类或定义一个矩阵的结构体,我采用的是后者:
最特殊的矩阵应该就是单位矩阵E了,它的对角线的元素为1,非对角线元素为0。
若A为n×p矩阵,B为p×m矩阵,则它们的乘积AB(有时记做A·B)将是一个n×m矩阵。其乘积矩阵AB的第i行第j列的元素为:
一般矩阵乘法采用朴素的O(n^3)的算法:
矩阵加法就是简单地将对应的位置的两个矩阵的元素相加:
在ACM的题目中,我们一般考虑的是n阶方阵之间的乘法以及n阶方阵与n维向量(把向量看成n×1的矩阵)的乘法。矩阵乘法最重要的性质就是满足结合律,同时它另一个很重要的性质就是不满足交换率,这保证了矩阵的幂运算满足快速幂取模(A^k % MOD)算法:
假设k = 27,则k的二进制表示为11011,所以
,可以看出:k的二进制的每一位矩阵A都要平方,在k二进制为1的位:末矩阵×平方后的A,在k二进制为0的位则末矩阵×E(单位矩阵),即不变。代码如下:
许多题目还要求S = A + A2 + A3 + … + Ak.。其实再作一次二分即可:只需计算log(n)个A的幂即可。
矩阵在ACM里用处最大的就是加速常系数递推方程的计算,最经典的例子就是Fibonacci数列,如果普通的递推,计算第n项复杂度为o(n),显然对于10^9左右的数据就力不从心了。如果用矩阵快速幂递推的话,计算第n项的复杂度为o(k3*log(n)),对应一般的题目已经绰绰有余了,有的题目可以根据矩阵的特殊性进行优化,可以达到o(k2*log(n))。
由F[n] = F[n-1] + F[n-2],常系数递推式右边有两项,所以向量和矩阵的规格都为2。容易如下递推:
实现计算Fibonacci数列第k项的代码如下:
转自http://hi.baidu.com/matrush/blog/item/ed9e7f979287ac037af480c7.html
附上自己的模板:
/**
*注意:这里只考虐方阵情况
**/
int n;//方阵维数
struct Mat{
int mat[N][N];
}E;
//初始化单位矩阵
Mat init(){
Mat E;
for(int i = 0; i < N; i++){
for(int j = 0; j < N; j++){
if(i == j)
E.mat[i][i] = 1;
else
E.mat[i][j] = 0;
}
}
return E;
}
//重载乘法
Mat operator *(Mat a,Mat b){
Mat c;
memset(c.mat,0,sizeof(Mat));
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
for(int k = 0; k < n; k++){
if(a.mat[i][k] && b.mat[k][j]){
c.mat[i][j] += a.mat[i][k] * b.mat[k][j];
}
}
}
}
return c;
}
//重载加法
Mat operator +(Mat a,Mat b){
Mat c;
memset(c.mat,0,sizeof(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];
}
}
return c;
}
//矩阵快速幂
Mat operator ^(Mat A,int x){
Mat c;
c = init();
for(; x ; x >>= 1){
if(x&1){
c = c*A;
}
A = A*A;
}
return c;
}
//求S = A + A^2 + A^3 + … + A^k
Mat sum(Mat A,int x){
if(x == 1){
return A;
}
if(x&1){
return (A^x)+sum(A,x - 1);
}
else{
return sum(A,x/2) + ((A^(x/2)) + E);
}
}
//求Fibonacci数列第x项(1,1,2,3,5,8....)
int Fib(int x){
if(x == 1 || x == 2)return 1;
Mat EE;
EE.mat[0][0] = 0;
EE.mat[0][1] = 1;
EE.mat[1][0] = 1;
EE.mat[1][1] = 1;
int beg[2] = {1,
1};
EE = EE^(x - 2);
int s1 = EE.mat[0][0]*beg[0] + EE.mat[0][1]*beg[1];
int s2 = EE.mat[1][0]*beg[0] + EE.mat[1][1]*beg[1];
return s2;
}
另一个模板,使用范围更广
struct Mat{
int n,m;
int mat[5][5];
Mat(){
memset(mat,0,sizeof(mat));
n = m = 5;
};
};
Mat operator *(Mat a,Mat b){
Mat c;
c = Mat();
c.n = a.n,c.m = b.m;
for(int i=1;i<=a.n;i++){
for(int j=1;j<=b.m;j++){
for(int k=1;k<=a.m;k++){
c.mat[i][j] += (a.mat[i][k]*b.mat[k][j])%MOD;
c.mat[i][j] %= MOD;
}
}
}
return c;
}
Mat operator +(Mat a,Mat b){
Mat c;
c = Mat();
c.n = a.n,c.m = a.m;
for(int i=1;i<=a.n;i++){
for(int j=1;j<=a.m;j++){
c.mat[i][j] = a.mat[i][j]+b.mat[i][j];
}
}
return c;
}
Mat operator ^(Mat a,int k){
Mat c;
int i,j;
c = Mat();
c.n = a.n,c.m = a.n;
for(i=1;i<=a.n;i++)c.mat[i][i] = 1;
while(k){
if(k&1){
c = c*a;
}
a = a*a;
k>>=1;
}
return c;
}
void out(Mat a){
int i,j;
for(i=1;i<=a.n;i++){
for(j=1;j<=a.m;j++){
cout<<a.mat[i][j]<<" ";
}
cout<<endl;
}
}