給定若干個矩陣,尋找最優的相乘次序,使得乘法運算的次數最少,並輸出對應的最少運算次數。好比現有三個矩陣ABC,維數分別爲\(A:2\times10\),\(B:10\times2\), \(C:2\times10\) 。雖然(AB)C=A(BC) 結果是相等的,即與相乘次序沒有關係。可是(AB)C乘法運算的次數爲\(2\times10\times2+2\times2\times10=80\),而A(BC)爲\(10\times2\times10+2\times10\times10=400\),顯然(AB)C的運算次數更少,即效率更高。ios
求解的關鍵在於如何將問題分解爲若干子問題。咱們想象在各個矩陣之間能夠放上隔板,那麼只要先分別求解左和右的最少乘法次數,再將隔板左右的兩部分相乘,就能夠獲得當前分隔方法的最優解,最後經過比較各類分隔方法,就能獲得當前長度的最優解,以下圖中四種分隔中咱們取最少的次數,即爲長度5(5個連續矩陣)的最優解。因爲分隔後的長度一定小於當前長度,如此處的子問題長度一定小於5,而子問題已經在以前的迭代過程求得,無需重複計算。經過下一部分的迭代過程展現能有更直觀的理解。算法
給定5個矩陣: ABCDE ,維數分別爲\(A:30\times35\) , \(B:35\times15\), \(C:15\times5\) , \(D:5\times10\), \(E:10\times20\) 。從矩陣鏈長度爲1開始求解,而後求解長度爲2的,最終到達5。dp[]數組(詳見算法實現)的更新以下圖所示。
爲了更直觀的理解,下圖和上面是等效的。(符號說明:m[a,b]表明從序號爲a到序號爲b的矩陣鏈所需的最少乘法次數,特別地,m[a,a]表明a號矩陣自己,很明顯m[a,a]=0。)
數組
arr[]數組用於記錄矩陣鏈信息,其中n號矩陣對應的維數是arr[n-1]*arr[n]。動態規劃的核心算法利用3個for循環,最外層控制矩陣鏈長度,下一層控制起始點,再下一層控制隔板的位置。最後左右合併的時候注意下標
的選擇,以下圖所示。
es5
#include <iostream> #include <limits> #include <cstring> using namespace std; int dp[51][51]; //從1開始,不使用0 // output: Minimum number of multiplications is 11875 /*功能:尋找矩陣鏈最優的運算次數 返回值:相對應的運算次數 */ int MinMatrixMul(int arr[], int num){ int start, mid, end; int times; //記錄當前的最優解,即最少的運算次數 //dp[i][i]=0 已經在初始化的時候完成 //dp[i][j] 表明i號到j號矩陣鏈的運算次數 for (int length = 2; length < num; ++length) { for (start = 1; start <= num-length; ++start) { end = start + length - 1; dp[start][end] = INT_MAX; for (mid = start; mid < end; ++mid) { times = dp[start][mid] + dp[mid+1][end] + arr[start-1]*arr[mid]*arr[end]; //若是找到當前長度下更優的解,馬上更新dp數組 if (times < dp[start][end]) { dp[start][end] = times; } } } } return dp[1][num-1]; } int main() { int num; int arr[] = {30, 35, 15, 5, 10, 20}; memset(dp, 0, sizeof(dp)); num = sizeof(arr)/ sizeof(int); cout << "Minimum number of multiplications is "<< MinMatrixMul(arr, num) << endl; }
思路:增長bracket數組,bracket[start][end]記錄從start到end當中的mid,即加括號的地方。若start==end說明只有一個矩陣,直接輸出。令name(使用引用
)從'A'開始,一旦輸出一個,便執行++name。spa
#include <iostream> #include <limits> #include <cstring> using namespace std; int dp[51][51]; //從1開始,不使用0 int bracket[51][51]; //記錄括號添加的位置 /*output: * Minimum number of multiplications is 11875 * Order: ((A(BC))(DE)) * */ /*功能:打印括號 *返回值:無 */ void printBrackets(int start, int end, char &name){ if (start == end) { cout << name; ++name; return; } cout << "("; printBrackets(start, bracket[start][end], name); printBrackets(bracket[start][end]+1, end, name); cout << ")"; } /*功能:尋找矩陣鏈最優的運算次數 *返回值:相對應的運算次數 */ int MinMatrixMul(int arr[], int num){ int start, mid, end; int times; //記錄當前的最優解,即最少的運算次數 //dp[i][i]=0 已經在初始化的時候完成 //dp[i][j] 表明i號到j號矩陣鏈的運算次數 for (int length = 2; length < num; ++length) { for (start = 1; start <= num-length; ++start) { end = start + length - 1; dp[start][end] = INT_MAX; for (mid = start; mid < end; ++mid) { times = dp[start][mid] + dp[mid+1][end] + arr[start-1]*arr[mid]*arr[end]; //若是找到當前長度下更優的解,馬上更新dp數組 if (times < dp[start][end]) { dp[start][end] = times; //更新添加括號的位置 bracket[start][end] = mid; } } } } return dp[1][num-1]; } int main() { int num; char name = 'A'; int arr[] = {30, 35, 15, 5, 10, 20}; memset(dp, 0, sizeof(dp)); num = sizeof(arr)/ sizeof(int); cout << "Minimum number of multiplications is "<< MinMatrixMul(arr, num) << endl; cout << "Order: " ; printBrackets(1, num-1, name); }
http://www.geeksforgeeks.org/dynamic-programming-set-8-matrix-chain-multiplication/code
http://www.geeksforgeeks.org/printing-brackets-matrix-chain-multiplication-problem/blog