《算法导论》:动态规划学习笔记

灰太狼 2023-07-10 15:58 28阅读 0赞

参考资料:《算法导论》

动态规划方法

动态规划方法用于解决这样一类问题:

  • 问题具有最优子结构性质,即如果这个问题达到了最优解,那么此时构成它的子问题也应当是最优解, 这与贪心算法类似
  • 子问题空间应该足够”小”, 即问题的递归算法会反复求解相同的子问题,而不是一直生成新的子问题
  • 可能需要尝试不同的划分方法,将问题分解成不同的子问题,以求得全局最优解

一般求解步骤:

  • 刻画一个最优解的结构特征
  • 递归地定义最优解的值
  • 通常采用自底向上的方法计算最优解的值
  • 构造最优解

问题求解

这里记录了书上提到的三个例子

  • 钢条切割问题展示了动态规划中等价的两种实现

    • 带备忘的自顶向下
    • 自底向上
  • 矩阵链乘法和最长公共子序列展示了动态规划问题的一般分析方法

1.钢条切割问题

一个长度为n的钢条,n为整数,切割为多个长度仍为整数的子钢条,每种长度的钢条价格不一,有一个记录每种长度的钢条价格的价格表p, 求最优切割方案
由条件可以推导出以下结论

  • 如果长度为n的钢条价格足够高,则无需切割
  • 共有 2 n − 1 2^{n-1} 2n−1种不同的切割方案,切割数目m从0到n,每种数目对应 C n − 1 m C_{n-1}^m Cn−1m种方法,求和得 2 n − 1 2^{n-1} 2n−1

递归实现

思路: 将钢条在下标 i i i处分为左右两部分,(因为总会有一个分割点,不切割的话 i i i=0) ,那么可以只对右边长度为 n − i n-i n−i的部分进行递归处理,若用 f ( n ) f(n) f(n)表示长度为 n n n的钢条的最佳切割方案,则有:
f ( n ) = m a x ( p i + f ( n − i ) ) 0 < = i < = n f(n) = max(p_i+f(n-i)) _{0<=i<=n} f(n)=max(pi+f(n−i))0<=i<=n
时间复杂度: 由于有 2 n − 1 2^{n-1} 2n−1种分法,递归的解法实际上尝试了所有的分法,所以时间复杂度: O ( 2 n − 1 ) O(2^{n-1}) O(2n−1)
在这里插入图片描述

分析: 递归的解法之所以慢,是因为在递归时重复求解了子问题,如果使用一个表记录子问题的结果,就可以提升求解效率

动态规划方法实现

1.自顶向下
数组r保存了从0到n每个长度的最优方案,以确保不会重复计算。本例中这种方法分析起来不如自底向上清晰

  1. MEMOIZED-CUT-ROD(p,n)
  2. let r[0...n] be a new array
  3. for i=0 to n:
  4. r[i] = INT_MIN
  5. return MEMOIZED-CUT-ROD-AUX(p,n,r)
  6. MEMOIZED-CUT-ROD-AUX(p,n,r)
  7. if r[n] >= 0:
  8. return r[n]
  9. if n == 0:
  10. q = 0
  11. else:
  12. q = MIN
  13. for i=1 to n:
  14. q = max(q,p[i]+MEMOIZED-CUT-ROD-AUX(p,n-i,r))
  15. r[n]=q
  16. return q

在这里插入图片描述
2.自底向上
自底向上方法同样使用数组r保存从0到n每个长度的最优方案,思路是一样的,但是结构更清晰,上一个方法由于使用了递归,分析起来比较麻烦。

  1. BOTTOM-UP-CUT-ROD(p, n)
  2. let r[0...n] be a new array
  3. r[0]=0
  4. for j=1 to n:
  5. q = MIN
  6. for i=1 to j:
  7. q=max(q,p[i]+r[j-i])
  8. r[j]=q
  9. return r[n]

在这里插入图片描述

2.矩阵链乘法

在这里插入图片描述

步骤1: 最优方案的结构特征

一个 m ∗ n m*n m∗n的矩阵和一个 n ∗ k n*k n∗k的矩阵相乘,需要进行 m n k mnk mnk次乘法运算
假设存在最优划分点k, 那么k的左右两边都是最优划分的

步骤2: 递归求解方案

若用 m [ i , j ] m[i, j] m[i,j]表示计算 A i . . . A j A_i…A_j Ai…Aj的最优解,那么在 i i i与 j j j范围内寻找一个k, 假设这个k是计算 A i . . . A j A_i…A_j Ai…Aj的最优划分,那么就把问题再次分解成了更小的子问题,且存在下面的递推关系,使用自底向上就可以完成求解
在这里插入图片描述

步骤3: 计算最优代价

输入是一个序列 p = < p 0 , p 1 , p 2 , . . . , p n > p= p=
使用辅助表 m [ 1… n , 1… n ] m[1…n,1…n] m[1…n,1…n] 保存代价 m [ i , j ] m[i,j] m[i,j]
另一个辅助表 s [ 1… n − 1 , 2… n ] s[1…n-1,2…n] s[1…n−1,2…n] 记录最优值 m [ i , j ] m[i,j] m[i,j]对应的分割点k

  1. MATRIX-CHAIN-ORDER(p)
  2. n = p.length - 1
  3. let m[1...n, 1...n] and s[1...n-1, 2...n] be new tables
  4. for i = 1 to n:
  5. m[i,i] = 0 // 当长度为0时代价为0
  6. for l = 2 to n: // l是子链的长度
  7. for i = 1 to n-l+1:
  8. j = i + l -1
  9. m[i,j] = MAX
  10. for k = i to j-1:
  11. q = m[i, k] + m[k+1,j] + p[i-1]*p[k]*p[j]
  12. if q < m[i, j]:
  13. m[i, j] = q
  14. s[i, j] = k

在这里插入图片描述

从长度为1、2的子链代价开始计算,逐步推导出完整的代价辅助表m

步骤4: 构造最优解
  1. PRINT-OPTIMAL-PARENS(s, i , j)
  2. if i==j:
  3. print "A[i]"
  4. else:
  5. print "("
  6. PRINT-OPTIMAL-PARENS(s, i, s[i,j])
  7. PRINT-OPTIMAL-PARENS(s, s[i,j]+1,j)
  8. print ")"

3.最长公共子序列

  • 子序列: 从原序列的下标集合中选取一部分递增的下标序列,以此构建的子序列
  • 公共子序列: X和Y存在相同的子序列Z
  • 注意点: 这里的子序列未必是原序列中连续的一部分

在这里插入图片描述

步骤1:最长公共子序列特征刻画

在这里插入图片描述

步骤2:递归解

在这里插入图片描述

步骤3: 计算LCS长度
  1. LCS-LENGTH(X, Y)
  2. m = X.length
  3. n = Y.length
  4. let b[1...m,1...n] and c[0...m,0...n] be new tables
  5. for i = 1 to m:
  6. c[i, 0] = 0
  7. for j = 1 to n:
  8. c[0,j] = 0
  9. for i = 1 to n:
  10. for j = 1 to m:
  11. if x[i] == y[j]:
  12. c[i,j] = c[i-1,j-1] + 1
  13. b[i,j] = "\"
  14. elseif c[i-1,j] >= c[i, j-1]:
  15. c[i,j] = c[i-1,j]
  16. b[i,j] = "|"
  17. else:
  18. c[i,j] = c[i,j-1]
  19. b[i,j] = "--"
  20. return c, b

b 的作用是辅助打印出LCS

步骤4: 构造LCS
  1. PRINT-LCS(b,X,i,j)
  2. if i==0 or j==0:
  3. return None
  4. if b[i, j] == "\":
  5. PRINT-LCS(b,X,i-1,j-1)
  6. print x[i]
  7. elseif b[i,j] == "|":
  8. PRINT-LCS(b,X,i-1,j)
  9. else:
  10. PRINT-LCS(b,X,i,j-1)

发表评论

表情:
评论列表 (有 0 条评论,28人围观)

还没有评论,来说两句吧...

相关阅读

    相关 算法笔记(二):动态规划

    一、基本思想 动态规划与分治法由相似之处,动态规划在求解子问题时也需要将原问题分解为子问题,首先求子问题的解,然后在此基础上求解原问题的解。然而,分治法中子问题与与原问题

    相关 算法学习笔记】-动态规划

    动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。在这种情况下,分治法会做许多不必要的工作,它会反复地求解