给定若干个矩阵,寻找最优的相乘次序,使得乘法运算的次数最少,并输出对应的最少运算次数。好比现有三个矩阵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