一個矩陣就是一個二維數組,爲了方便聲明多個矩陣,我們一般會將矩陣封裝一個類或定義一個矩陣的結構體,我採用的是後者:
最特殊的矩陣應該就是單位矩陣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;
}
}