题目
给定n个矩阵{A1,A2,…,An}(其中,矩阵Ai的维数为pi-1*pi,i=1,2,3,…,n),如何确定计算矩阵的连乘积A1,A2,…,An的计算次序(完全加括号方式),使得此次序计算矩阵连乘积需要的数乘次数最少。
步骤
-
分析最优解的结构
将矩阵连乘积AiAi+1…Aj简记为A[i:j],则原式记为A[1:n]。
将矩阵链在Ak和Ak+1之间断开,1≤k<n,则AiAi+1…Aj=(A1...Ak)(A1...Ak)。我们只要计算A[1:k]以及A[k+1:n],再将二者相乘则可得出结果。
我们只需要保证A[1:k]以及A[k+1:n]的计算次序也是最优的,那么A[1:n]的计算次序也就是最优的了(最优子结构的性质)。
-
建立递归关系
计待求问题最优值的乘积次数为m[i][j]。
i = j 时,A[i: j]=Ai,m[i][j]=0,i=1,2,…,n
i < j 时,根据矩阵数乘的性质得:m[ i ][ j ] = m[ i ][ k ] + m[ k+1][ j ] + pi-1 pk pj
又因为k的位置未定,而k的可能取值范围为i,i+1,…,j-1,因此k是这j-1个位置中使得m[ i ][ j ]的值达到最小的位置。可以得到递推关系
i < j 时 ,m[ i ][ j ] = min { m[ i ][ k ] + m[ k+1][ j ] + pi-1 pk pj } , i ≤ k < j
将对应m[ i ][ j ]的断开位置k记为s[ i ][ j ],则之后可递归地由s[ i ][ j ]构造出相应的最优解
-
计算最优值
依据其递归式,自底向上的计算。
下列算法中,输入参数{p0,p1,…,pn}。

1 void MatrixChain(int *p, int n, int**m, int **s){ 2 for{int i=1;i<=n;i++) m[i][j]=0; 3 for(int r=2;r<=n;r++){ 4 for(int r =2;r<=n;r++){ 5 int j=i+r-1; 6 m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j]; 7 s[i][j] = i; 8 for(int k =i+1;k<j;k++){ 9 int t = m[i][k]+m[k+1][j] + p[i-1]*p[i]*p[j]; 10 if(t< m[i][j]){ m[i][j] =t ; s[i][j] =k;} 11 } 12 } 13 } 14 }
该算法先计算出m[ i ][ i ],然后依次计算m[ i ][ i+1 ],m[ i ][ i+2 ],…,m[ i ][ j ]。在计算m[ i ][ j ]时,只需要用到之前计算过的m[ i ][ k ]和m[ k+1 ][ j ]。
算法的主要计算量取决于r,i,k的三重循环,因此时间复杂度为O(n3),空间复杂度为O(n2)。
-
构造最优解
s[ i ][ j ]表示A[i: j]的最佳断开位置为Ak和Ak+1。
因此A[1: n]的最佳加括号方式为(A[1: s[ 1 ][ n ]) (A[s[ 1 ][ n ]+1: n]) //s[ i ][ j ]存的是相应的k
A[1: s[ 1 ][ n ] ] 的最佳加括号方式为(A[ 1 : s[ 1 ][ s[ 1 ][ n ] ] ]) (A[ s[ 1 ][ s[ 1 ][ n ] ] +1 : s[ 1 ][ n ])
递推。

1 void Traceback(int i, int j, int **s){ 2 if(i==j) return; 3 Traceback(i, s[ i ][ j ],s); 4 Traceback(s[ i ][ j ]+1 ,j ,s); 5 6 cout<<"Multiply A" <<i<<", "<<s[ i ][ j ]; 7 cout<<" and A "<<(s[ i ][ j ]+1)<<", "<<j<<endl; 8 }
要输出A[1: n]的最优计算次序只需要调用Traceback(1 , n, s)即可