題目
給定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)即可