C++动态规划
2010-11-16 13:05:18 来源:WEB开发网核心提示:计算过程为:从m可知最小连乘次数为m[1][6] = 15125从s可知计算顺序为((A1(A2A3))((A4A5))A6))实现:/* 主题:矩阵连乘问题* 作者:chinazhangjie* 邮箱:chinajiezhang@gmail.com* 开发语言:C++* 开发环境:Mircosoft Virsual
计算过程为:
从m可知最小连乘次数为m[1][6] = 15125
从s可知计算顺序为((A1(A2A3))((A4A5))A6))
实现:
/* 主题:矩阵连乘问题* 作者:chinazhangjie* 邮箱:chinajiezhang@gmail.com* 开发语言:C++* 开发环境:Mircosoft Virsual Studio 2008* 时间: 2010.11.14*/#include <iostream>#include <vector>using namespace std ;class matrix_chain{public: matrix_chain(const vector<int> & c) { cols = c ; count = cols.size () ; mc.resize (count) ; s.resize (count) ; for (int i = 0; i < count; ++ i) { mc[i].resize (count) ; s[i].resize (count) ; } for (int i = 0; i < count; ++ i) { for (int j = 0; j < count; ++ j) { mc[i][j] = 0 ; s[i][j] = 0 ; } } } // 使用备忘录方法计算 void lookup_chain () { __lookup_chain (1, count - 1) ; min_count = mc[1][count - 1] ; cout << "min_multi_count = "<< min_count << endl ; // 输出最优计算次序 __trackback (1, count - 1) ; } // 使用普通方法进行计算 void calculate () { int n = count - 1; // 矩阵的个数 // r 表示每次宽度 // i,j表示从从矩阵i到矩阵j // k 表示切割位置 for (int r = 2; r <= n; ++ r) { for (int i = 1; i <= n - r + 1; ++ i) { int j = i + r - 1 ; // 从矩阵i到矩阵j连乘,从i的位置切割,前半部分为0 mc[i][j] = mc[i+1][j] + cols[i-1] * cols[i] * cols[j] ; s[i][j] = i ; for (int k = i + 1; k < j; ++ k) { int temp = mc[i][k] + mc[k + 1][j] + cols[i-1] * cols[k] * cols[j] ; if (temp < mc[i][j]) { mc[i][j] = temp ; s[i][j] = k ; } } // for k } // for i } // for r min_count = mc[1][n] ; cout << "min_multi_count = "<< min_count << endl ; // 输出最优计算次序 __trackback (1, n) ; }private: int __lookup_chain (int i, int j) { // 该最优解已求出,直接返回 if (mc[i][j] > 0) { return mc[i][j] ; } if (i == j) { return 0 ; // 不需要计算,直接返回 } // 下面两行计算从i到j按照顺序计算的情况 int u = __lookup_chain (i, i) + __lookup_chain (i + 1, j) + cols[i-1] * cols[i] * cols[j] ; s[i][j] = i ; for (int k = i + 1; k < j; ++ k) { int temp = __lookup_chain(i, k) + __lookup_chain(k + 1, j) + cols[i - 1] * cols[k] * cols[j] ; if (temp < u) { u = temp ; s[i][j] = k ; } } mc[i][j] = u ; return u ; } void __trackback (int i, int j) { if (i == j) { return ; } __trackback (i, s[i][j]) ; __trackback (s[i][j] + 1, j) ; cout <<i << "," << s[i][j] << " " << s[i][j] + 1 << "," << j << endl; }private: vector<int> cols ; // 列数 int count ; // 矩阵个数 + 1 vector<vector<int> > mc; // 从第i个矩阵乘到第j个矩阵最小数乘次数 vector<vector<int> > s; // 最小数乘的切分位置 int min_count ; // 最小数乘次数} ;int main() { // 初始化 const int MATRIX_COUNT = 6 ; vector<int> c(MATRIX_COUNT + 1) ; c[0] = 30 ; c[1] = 35 ; c[2] = 15 ; c[3] = 5 ; c[4] = 10 ; c[5] = 20 ; c[6] = 25 ; matrix_chain mc (c) ; // mc.calculate () ; mc.lookup_chain () ; return 0 ;}
算法复杂度分析:
算法matrixChain的主要计算量取决于算法中对r,i和k的3重循环。循环体内的计算量为O(1),而3重循环的总次数为O(n^3)。因此算法的计算时间上界为O(n^3)。算法所占用的空间显然为O(n^2)。
动态规划算法的基本要素
一、最优子结构
矩阵连乘计算次序问题的最优解包含着其子问题的最优解。这种性质称为最优子结构性质。
在分析问题的最优子结构性质时,所用的方法具有普遍性:首先假设由问题的最优解导出的子问题的解不是最优的,然后再设法说明在这个假设下可构造出比原问题最优解更好的解,从而导致矛盾。
利用问题的最优子结构性质,以自底向上的方式递归地从子问题的最优解逐步构造出整个问题的最优解。最优子结构是问题能用动态规划算法求解的前提。
同一个问题可以有多种方式刻划它的最优子结构,有些表示方法的求解速度更快(空间占用小,问题的维度低)
二、重叠子问题
递归算法求解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。这种性质称为子问题的重叠性质。
动态规划算法,对每一个子问题只解一次,而后将其解保存在一个表格中,当再次需要解此子问题时,只是简单地用常数时间查看一下结果。
通常不同的子问题个数随问题的大小呈多项式增长。因此用动态规划算法只需要多项式时间,从而获得较高的解题效率。
三、备忘录方法
备忘录方法的控制结构与直接递归方法的控制结构相同,区别在于备忘录方法为每个解过的子问题建立了备忘录以备需要时查看,避免了相同子问题的重复求解。
实现(见矩阵连乘源码)
实例二、最长公共子序列
若给定的序列X = {x1,x2,…,xm},则另一序列Z = {z1,z2,…,zk},是X的子序列是指存在一个严格下表序列{i1,i2,…,ik}使得对于所有的j = 1,2,…k有zj = xij。例如,序列Z = {B,C,D,B}是序列X = {A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。
[]
赞助商链接