动态规划的思想:
- 动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题
- 动态规划分解得到的子问题往往不是相互独立的。不同子问题的数目常常只有多项式量级
动态规划的基本要素:
- 最优子结构
- 矩阵连乘计算次序问题的最优解包含着其子问题的最优解———最优子结构性质
- 以自底向上的方式递归地从子问题的最优解逐步构造出整个问题的最优解
- 子问题的重叠性质
- 对每一个子问题只解一次,而后将其解保存在一个表格中(可急
分治法和动态规划的区别:
- 两者都是通过将整个问题逐步划分为一个个小的问题进行解决
- 分治法:每个子问题都是相互独立(因此会出现很多的重复计算)
- 动态规划:每个子问题不是相互独立,并会将子问题的解进行储存,避免重复计算,从而提高效率
- 动态规划从部分到整天,而分治思想从整体到部分
例如:
斐波那契数列:
有很多子问题会被重复计算,从而降低了效率
矩阵连乘
矩阵连乘是动态规划的一个简单的应用
下面运用了3种不同的方式,对比动态规划与递归的区别,已经动态规划思想是如何得到应用的
递归:
recurMatrixChain(int i, int j){
if(i==j) return 0
int u = recurMatrixChain(i+1,j)+p[i-1]*p[i]*p[j]
S[i][j]=i
for(int k=i+1;k<j;k++)
int t = recurMatricChain(i,k)+recurMatricChain(k+1,j)+p[i-1]*p[i]*p[j]
if(t<u){
u=t
S[i][j]=k
}
return u
method1,method2,method3 就是这道题递归的划分思想
备忘录:
相比于递归,备忘录就是增添一个数组m,去记录每次计算的结果,从而减少了很多重复的计算,提高了效率
备忘录是自上而下的记录方式
m <- 0
private static int lookupChain( i, 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 = lookupChain(i,k)+lookupChain(k+1,j)+p[i-1]*p[i]*p[j]
if(t<u){
u=t
S[i][j]=k
}
#记录每个已经运算的子问题
m[i][j] = u
return u
动态规划:
相比于递归和备忘录,动态规划则采用的是自下而上的计算思想,和存储方式
public static void matrixChain(int[] p, int[][] m, int[][] s){
int n=p.length-1
#计算最小规模子问题
for(int i=1;i<=n;i++) m[i][i] =0
#计算规模从2到n,规模逐渐增大的各子问题
for(int r=2;r<=n;r++){
#按照r规模划分,
#A1,A2,A3,A4
#当r = 2 时
#for划分为(A1,A2)(A2,A3)(A3,A4)
for(int i=1;i<=n-r+1;i++){
int j=i+r-1;
# j始终比i大r-1
m[i][j] = m[i+1][j]+p[i-1]*p[i]*p[j]
s[i][j]=i
#计算r个规模中最小的计算量
#例如(A1,A2,A3)
#先计算A1(A2,A3) 的大小
#再计算(A1,A2)A3 的大小
#并比较,留小的那个值
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;