题目:矩阵连乘, 求解计算量最小的加括号方法。
输入: 按顺序输入各个矩阵的 行和列。
求解: 最少数乘次数和加括号方案。
(详细情况可自行百度)
题解: 用一个数组p[] 来存储参数, (但要进行处理一下, 如: 矩阵A 为 10*5 , B 为 5*15。P【】只记录 10, 5, 15)
用m【i】【j】 来表示 从 i->j 的数乘最小值。s[][]记录过程中的最优步骤.
const int maxn = 100; int p[maxn], m[maxn][maxn], s[maxn][maxn] void matrixChain(p[], int m[][maxn], int s[][maxn]) { int n=p.length-1; for(int i=1; i<=n; i++) m[i][i] = 0; for(int r=2; r<=n; r++)//控制行和列 for(int i=1; i<=n-r+1; i++)//行 { int j = i+r-1;//列 m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j]//m[i][i]=0, 未加上。 s[i][j] = i; for(int k=i+1; k<j; k++) { int 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; } } } }
构造最优解:
void traceback(int s[][maxn], int i, int j) { if(i==j) return; traceback(s, i, s[i][j]); traceback(s, s[i][j]+1, j); printf("(%d, %d) (%d, %d)", i, s[i][j], s[i][j]+1, j); } traceback(s, i, j);
备忘录法:
int memoizedmatrixChain(int n) { for(int i=1; i<=n; i++) for(int j=i; j<=n; j++) m[i][j] = 0; return lookupChain(1, n); } int lookupChain(int i, int j) { if(m[i][j]>0) return m[i][j]; if(i==j) return 0; int u=lookupChain(i+1, j) + p[i-1]*p[i]*p[j]; s[i][j] = i; for(int k=i+1; k<j; k++) { int t = lookChain(i, k) + lookupChina(k+1, j) + p[i-1]*p[k]*p[j]; if(t<u) { u=t; s[i][j] = k; } } m[i][j] = u; return u; }
动态规划和备忘录法的区别:
备忘录法是动态规划的变形。 与动态规划算法一样, 备忘录方法用表格保存已解决的子问题的答案。 在下次需要解此问题时,只要简单的查看该子问题的解答, 而不必重新计算。
不同点:
与动态规划算法不同的是, 备忘录方法色递归方式是自顶向下的, 而动态规划是自底向上的递归的。 因此备忘录方法的控制结构与直接递归方法的控制结构相同, 区别在于备忘录方法为每个解过的子问题建立了备忘录以备需要时查看,避免了相同子问题的重复求解。
各有千秋:
一般情况下, 当一个问题的所有子问题都要至少解一次时,用动态规划算法比备用录方法好, 此时, 动态规划算法没有任何多余的计算。 同时, 对于许多问题, 常可利用其规则的表格存取方式,减少动态规划算法的计算时间和空间需求。 当子问题空间中的部分子问题可不必求解时, 用备忘录方法则较有利, 因为从其控制结构可以看出, 该方法只解那些确实需要求解的子问题。