话不多说,直接代码:
void MatrixChain(int *p, int n, int m[][maxn], int s[][maxn])
{
int i, j, k, r, t;
for(i = 1; i <= n; i++)
m[i][i] = 0;
for(r = 2; r <= n; r++)
for(i = 1; i <= n-r+1; i++)
{
j = i+r-1;
m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j];
s[i][j] = i;
for(k = i+1; k < j; k++)
{
t = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
if(t < m[i][j])
{
m[i][j] = t;
s[i][j] = k;
}
}
}
}
算法复杂度分析:
算法matrixChain的主要计算量取决于算法中对r,i和k的3重循环。循环体内的计算量为O(1),而3重循环的总次数为O(n3)。因此算法的计算时间上界为O(n3)。算法所占用的空间显然为O(n2)。
•算法MatrixChain仅给出矩阵连乘积所需的最少数乘次数, 但未给出具体按什么次序做矩阵乘法.
•但注意到: MatrixChain已记录了构造一个最优解所需的全部信息: 由s[i,j]中的数k知, 计算矩阵链A[i,j]的最佳方式应在Ak和Ak+1之间断开.
•由算法matrixChain中s[i][j] = k 知,矩阵链A[i:j]的最优加括号方式应为(A[i:k])(A[k+1:j])。计算A[1:n]的最优加括号方式为
• (A[1: s[1][n] ])( A[ s[1][n]+ 1 : n])。
• 而A[1: s[1][n] ]的最优加括号方式(A[1:s[1][s[1][n]]])(A[s[1][s[1][n]]+1:s[1][n])。……。由此可确定A[1:n]的最优完全加括号方式,即构造出问题的一个最优解。
P53 算法Traceback计算出断点矩阵s指示的加括号方式输出计算A[i][j] 的最优计算次序
void Traceback(int i, int j, int s[][maxn])
{
if(i == j)
return;
Traceback(i, s[i][j], s);
Traceback(s[i][j]+1, j, s);
printf("Multiply A%d,%d and A%d,%d\n", i, s[i][j], s[i][j]+1, j);
}
动态规划算法的基本要素:
一、最优子结构:
•问题的最优解包含着其子问题的最优解,这种性质称为最优子结构性质。
• 比如:矩阵连乘问题具有最优子结构性质。
• 分析问题是否具有最优子结构性质:首先假设由问题的最优解导出的子问题的解不是最优的,然后再设法说明在这个假设下可构造出比原问题最优解更好的解,从而导致矛盾。
• 利用问题的最优子结构性质,以自底向上的方式递归地从子问题的最优解逐步构造出整个问题的最优解。
•最优子结构是问题能用动态规划算法求解的前提。
同一个问题可以有多种方式刻划它的最优子结构,有些表示方法的求解速度更快(空间占用小,问题的维度低)
二、重叠子问题
•递归算法求解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。这种性质称为子问题的重叠性质。
•动态规划算法对每一个子问题只解一次,而后将其解保存在一个表格中,当再次需要解此子问题时,只是简单地用常数时间查看一下结果。
•通常不同的子问题个数随问题的大小呈多项式增长。
• 因此用动态规划算法只需要多项式时间,从而有较高的解题效率。