分析:
矩阵链乘法问题描述:给定由n个矩阵构成的序列[A1,A2,...,An],对乘积A1A2...An,找到最小化乘法次数的加括号方法。
1)寻找最优子结构
此问题最难的地方在于找到最优子结构。对乘积A1A2...An的任意加括号方法都会将序列在某个地方分成两部分,也就是最后一次乘法计算的地方,我们将这个位置记为k,也就是说首先计算A1...Ak和Ak+1...An,然后再将这两部分的结果相乘。
最优子结构如下:假设A1A2...An的一个最优加括号把乘积在Ak和Ak+1间分开,则前缀子链A1...Ak的加括号方式必定为A1...Ak的一个最优加括号,后缀子链同理。一开始并不知道k的确切位置,需要遍历所有位置以保证找到合适的k来分割乘积。
2)构造递归解└ min(i≤k<j) {m[i,k] + m[k+1,j] + p[i-1] * p[k] *p[j] } 如果i<j
3)构建辅助表,解决重叠子问题辅助表s[n][n]可以由2种方法构造,一种是自底向上填表构建,该方法要求按照递增的方式逐步填写子问题的解,也就是先计算长度为2的所有矩阵链的解,然后计算长度3的矩阵链,直到长度n;另一种是自顶向下填表的备忘录法,该方法将表的每个元素初始化为某特殊值(本问题中可以将最优乘积代价设置为一极大值),以表示待计算,在递归的过程中逐个填入遇到的子问题的解。
这里我使用c++中容器来解决,一般使用的数组下标带来的问题极其容易出差,还是c++比较方便,解决数组必须用常量来表示数组下标。不废话了,思路自己看算法导论树上的讲解,搞清解决问题原理写出代码就轻而易举。
#include<iostream> #include<vector> #include<algorithm> #include<iterator> using namespace std; pair<vector<vector<int>>,vector<vector<int>>> matrix_chain_order(vector<int> p) { int n = p.size(); vector<vector<int>> m(n, vector<int>(n, 0));//vector的vector相当于数组的数组,即二维数组m[][] vector<vector<int>> s(n, vector<int>(n, 0));//同上s[][] for (int len = 2; len < n; len++) { for (int i = 1; i < n - len + 1; ++i) { int j = i + len - 1; m[i][j] = 0x7fffffff; for (int k = i; k <= j-1; ++k) { int q = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j]; if (q < m[i][j]) { m[i][j] = q; s[i][j] = k; } } } } return make_pair(m, s); } //打印最优解 void print_Optimal_Parens(vector<vector<int> > s, int i, int j) { if (i == j) cout << "A" << i; else { cout << "("; print_Optimal_Parens(s, i, s[i][j]); print_Optimal_Parens(s, s[i][j] + 1, j); cout << ")"; } } //一般递归方法 int recursive_matrix_chain(vector<int> p, int i, int j) { int n = p.size(); vector<vector<int>> m(n, vector<int>(n, 0)); if (i == j) return 0; m[i][j] = 0x7fffffff; for (int k = i; k < j; k++) { int q = recursive_matrix_chain(p, i, k) + recursive_matrix_chain(p, k + 1, j) + p[i - 1] * p[k] * p[j]; if (q < m[i][j]) m[i][j] = q; } return m[i][j]; } //带备忘的自顶向下的递归方法实现 int look_up_chain(vector<vector<int>> m, vector<int> p, int i, int j) { if (m[i][j] < 0x7fffffff)//如果m[i][j]不是无穷大则说明m[i][j]已经被下一层的LookUpChain计算出来 return m[i][j]; if (i == j) m[i][j] = 0; for (int k = i; k < j; k++) { int q = look_up_chain(m, p, i, k) + look_up_chain(m, p, k + 1, j) + p[i - 1] * p[k] * p[j]; if (q < m[i][j]) m[i][j] = q; } return m[i][j]; } int memoized_matrix_chain(vector<int> p,int s,int e) { int n = p.size(); vector<vector<int>> m(n, vector<int>(n, 0)); for (int i = 1; i < n; i++) { for (int j = i; j < n; ++j) { m[i][j] = 0x7fffffff; } } return look_up_chain(m, p, s, e); } int main() { vector<int> p = { 30, 35, 15, 5, 10, 20, 25 }; vector<vector<int>> m = matrix_chain_order(p).first; vector<vector<int>> s = matrix_chain_order(p).second; cout << "vector m:" << endl; for (size_t i = 0; i < m.size(); i++) { copy(m[i].begin(), m[i].end(), ostream_iterator<int>(cout, " ")); cout << endl; } cout << "vector s:" << endl; for (size_t i = 0; i <s.size(); i++) { copy(s[i].begin(), s[i].end(), ostream_iterator<int>(cout, " ")); cout << endl; } cout << "输出最优解:————>"; print_Optimal_Parens(s, 1, 6); cout << endl; cout << "--------------------------------------------------" << endl;; vector<vector<int>> m1 = matrix_chain_order(p).first; vector<int> p1 = { 30, 35, 15, 5, 10, 20, 25 }; cout << "普通递归方法求解:"; cout << recursive_matrix_chain(p1, 1, 6) << endl; cout << "带备忘的递归方法求解:"; cout << look_up_chain(m1, p1, 1, 6) << endl; }
算法导论------------------动态规划之矩阵链问题
原文地址:http://blog.csdn.net/chenxun_2010/article/details/41926371