一、问题介绍:
给定由n个要相乘的矩阵构成的序列(链)<A1, A2, ... , An>,要计算乘积:
A1, A2...An
一组矩阵的乘积是加全括号的(fully parenthesized),如果它是单个矩阵,或是两个加全括号的矩阵的乘积外加括号而成。矩阵的乘法满足乘法的结合律,故无论怎样加括号都会产生相同的结果。但加括号的位置会影响标量乘法的个数,从而影响矩阵乘法的计算复杂度。
这里我们希望找到一种加括号的方式,使得矩阵链的乘法能够进行最少的标量乘法。
二、算法分析:矩阵链乘法问题是动态规划算法里一个比较经典的算法。
1、最优子结构:
Ai...j表示AiAi+1...Aj的乘积。对乘积AiAi+1...Aj的任何加全括号形式都将乘积在Ak与Ak+1之间分开,此处k是范围i <= k < j之内的一个整数。所以可以将AiAi+1...Aj分解成Ai..Ak与Ak+1...Aj的两个独立子问题。
2、 递归定义:
分析上面的递归式,我们发现其时间负责是指数级的,它与检查每一种加全括号乘积的强力算法差不多。但同时我们也可以看到原问题只有相当少的子问题:每一对问题满足1 <= i <= j <= n的i和j对应一个问题,总共O(n2)。一个递归算法在其递归树的不同分支中可能会遇到同一个子问题。子问题重叠这一性质是是否可以采用动态规划的第二个标志(第一个标志是最有子结构)。
这里我们采用了自底向上的方法。我们定义了矩阵p[i][j]来存储从Ai...Aj矩阵相乘的最小标量乘法数,s[i][j]存储Ai...Aj相乘加括号的位置,具体算法如下:
文件“h1.h"
#ifndef H1_H
#define H1_H
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<process.h>
#include<math.h>
#define MAXSIZE 100
#define INFINITY 1000000
struct matrix{
int row;
int column;
};
int chainValid(matrix m[], int n);
void matrixChain(matrix m[], int n, int p[][MAXSIZE], int s[][MAXSIZE]);
void Print_Optionmal_Parenthesize(int s[][MAXSIZE], int i, int j);
#endif
文件"MatrixChain.cpp"
#include "h1.h"
int chainValid(matrix m[], int n){
int i;
for(i=1; i<n-1; i++){
if(m[i].column != m[i+1].row){
return 0;
}
}
return 1;
}
void matrixChain(matrix m[], int n, int p[][MAXSIZE], int s[][MAXSIZE]){
int l, i, j, k, q;
for(i=1; i<=n; i++){
p[i][i] = 0;
}
for(l=2; l<=n; l++){
for(i=1; i<=n-l+1; i++){
j=i+l-1;
p[i][j] = INFINITY;
for(k=i; k<=j-1; k++){
q = p[i][k] + p[k+1][j] + m[i].row * m[k].column * m[j].column;
if(q < p[i][j]){
p[i][j] = q;
s[i][j] = k;
}
}
}
}
}
void Print_Optionmal_Parenthesize(int s[][MAXSIZE], int i, int j){
if(i == j){
printf(" A%d ", i);
}
else{
printf("(");
Print_Optionmal_Parenthesize(s, i, s[i][j]);
Print_Optionmal_Parenthesize(s, s[i][j]+1, j);
printf(")");
}
}
int main(){
matrix m[MAXSIZE];
int p[MAXSIZE][MAXSIZE];
int s[MAXSIZE][MAXSIZE];
m[1].row =30; m[1].column = 35;
m[2].row = 35; m[2].column = 15;
m[3].row = 15; m[3].column =5;
m[4].row = 5; m[4].column = 10;
m[5].row = 10; m[5].column = 20;
m[6].row = 20; m[6].column = 25;
if(!chainValid(m, 6)){
printf("Matrix Chain Invalid!\n");
return 0;
}
matrixChain(m, 6, p, s);
printf("%d \n", p[1][6]);
Print_Optionmal_Parenthesize(s, 1, 6);
return 0;
}
此算法的时间复杂度是O(n 3)。