[算法设计与分析-从入门到入土] 动态规划
知乎:https://www.zhihu.com/people/byzh_rc
CSDN:https://blog.csdn.net/qq_54636039
注:本文仅对所述内容做了框架性引导,具体细节可查询其余相关资料or源码
参考文章:各方资料
文章目录
- [[算法设计与分析-从入门到入土] 动态规划](#[算法设计与分析-从入门到入土] 动态规划)
- 动态规划
-
-
-
- 1.最长公共子序列问题LCS
- [2.全对最短路径问题(All-Pairs Shortest Path)](#2.全对最短路径问题(All-Pairs Shortest Path))
- [3. 0/1背包问题Knapsack](#3. 0/1背包问题Knapsack)
-
-
动态规划
算法本身并非递归算法,但问题的解通常以递归的形式来表达
核心: 采用 自底向上 的求解思路, 空间换时间
通过存储子问题的解以避免重复计算,是这一强大算法范式的基础
该类问题的解通常可表示为递推关系式, 若直接求解会导致子问题被重复计算
e.g.斐波那契序列, 应该从下往上来算 f ( n ) f(n) f(n)
动态规划算法会为原问题所考虑的每个子问题实例都计算出最优解 ,阐释了算法设计中的一个重要原则------最优性原理:给定一个最优决策序列,其中的每个子序列本身也必须是最优决策序列
1.最长公共子序列问题LCS
给定两个字符串A和B,其长度分别为n和m,且字符均来自字母表Σ。要求确定同时是A和B的最长子序列的长度
字符串 A = a 1 a 2 ... a n A=a_1a_2...a_n A=a1a2...an的子序列是指形如 a i 1 a i 2 ... a i k a_{i1}a_{i2}...a_{ik} ai1ai2...aik的字符串,其中每个 i j i_j ij满足 1 ≤ i j ≤ n 1≤i_j≤n 1≤ij≤n,且下标严格递增: 1 ≤ i 1 < i 2 < . . . < i k ≤ n 1≤i_1 < i_2 < ... < i_k ≤ n 1≤i1<i2<...<ik≤n
例子:
A: ALGORITHM
B: PROGRAM
->
长度为1的子序列: A, G, R, M
长度为2的子序列: OR, RM, AM
长度为3的子序列: GRM, ORM
动态规划:
设 A = a 1 a 2 ... a n A = a_{1}a_{2} \dots a_{n} A=a1a2...an 与 B = b 1 b 2 ... b m B = b_{1}b_{2} \dots b_{m} B=b1b2...bm。
令 L [ i , j ] L[i,j] L[i,j] 表示 a 1 a 2 ... a i a_{1}a_{2} \dots a_{i} a1a2...ai 与 b 1 b 2 ... b j b_{1}b_{2} \dots b_{j} b1b2...bj 的最长公共子序列的长度,其中 0 ⩽ i ⩽ n 0 \leqslant i \leqslant n 0⩽i⩽n, 0 ⩽ j ⩽ m 0 \leqslant j \leqslant m 0⩽j⩽m
(从0开始, 也就是空字符串开始)
递推关系:
- 若 a i = b j a_i = b_j ai=bj,则 L [ i , j ] = L [ i − 1 , j − 1 ] + 1 L[i,j] = L[i-1,j-1] + 1 L[i,j]=L[i−1,j−1]+1
(当前字符匹配,L为左上角L加一) - 若 a i ≠ b j a_i \neq b_j ai=bj,则 L [ i , j ] = max ( L [ i , j − 1 ] , L [ i − 1 , j ] ) L[i,j] = \max(L[i,j-1], L[i-1,j]) L[i,j]=max(L[i,j−1],L[i−1,j])
(当前字符不匹配,L为max(左边L,上边L)
用 ( n + 1 ) × ( m + 1 ) (n+1)×(m+1) (n+1)×(m+1) 的表格来计算 L [ i , j ] ( 0 ≤ i ≤ n , 0 ≤ j ≤ m ) L[i,j] \quad (0≤i≤n, 0≤j≤m) L[i,j](0≤i≤n,0≤j≤m)
显然,若 i = 0 i=0 i=0 或 j = 0 j=0 j=0,则 L [ i , j ] = 0 L[i,j]=0 L[i,j]=0
例子:
A=zxyxyz
B=xyyzx
| A(i) \ B(j) | 0 | x | y | y | z | x |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| z | 0 | 0 | 0 | 0 | 1 | 1 |
| x | 0 | 1 | 1 | 1 | 1 | 2 |
| y | 0 | 1 | 2 | 2 | 2 | 2 |
| x | 0 | 1 | 2 | 2 | 2 | 3 |
| y | 0 | 1 | 2 | 3 | 3 | 3 |
| z | 0 | 1 | 2 | 3 | 4 | 4(结果长度) |
第一行第一列都是0
时间复杂度: O ( n m ) O(nm) O(nm)
(需填充(n+1)×(m+1)个单元格,每个单元格计算耗时O(1))
空间复杂度: O ( m i n { m , n } ) O(min\{m,n\}) O(min{m,n})
(可优化为一维数组存储,仅保留前一行信息)
2.全对最短路径问题(All-Pairs Shortest Path)
设 G=(V,E) 为一个有向图,其中每条边 (i,j) 都有一个非负的长度 l\[i,j\]
(如果从顶点 i 到顶点 j 没有边,则 l\[i,j\] = \\infty )
目标: 找出每个顶点到所有其他顶点的距离
(从顶点 x 到顶点 y 的距离 是指从 x 到 y 的一条最短路径的长度)
假设 V = {1, 2, \\ldots, n} 。令 i 和 j 是 V 中两个不同的顶点
定义 d_{i,j}\^{k} 为从 i 到 j 且不经过任何属于集合 {k+1, k+2, \\ldots, n} 中顶点的最短路径的长度
递推关系:
d i , j k = { l [ i , j ] if k = 0 min { d i , j k − 1 , d i , k k − 1 + d k , j k − 1 } if 1 ≤ k ≤ n d_{i,j}^{k} = \begin{cases} l[i,j] & \text{if } k = 0 \\ \min \left\{ d_{i,j}^{k-1},\ d_{i,k}^{k-1} + d_{k,j}^{k-1} \right\} & \text{if } 1 \leq k \leq n \end{cases} di,jk={l[i,j]min{di,jk−1, di,kk−1+dk,jk−1}if k=0if 1≤k≤n
- i = j i=j i=j时, d i , j k = 0 d_{i,j}^k=0 di,jk=0(顶点到自身的距离为0)
- i = k o r j = k i=k ~or~j=k i=k or j=k时, d i , j k = d i , j k − 1 d_{i,j}^k=d_{i,j}^{k-1} di,jk=di,jk−1(经过顶点k与否不影响路径)
动态规划(弗洛伊德算法):
使用 n+1 个 n \\times n 维矩阵 D_{0}, D_{1}, D_{2}, \\ldots, D_{n} 来计算受限最短路径的长度
初始化: 设置 D_{0}\[i,i\] = 0
- 如果 i \\neq j 且 (i,j) 是 G 中的一条边,则 D_{0}\[i,j\] = l\[i,j\]
- 否则 D_{0}\[i,j\] = \\infty
进行 n 次迭代,使得在第 k 次迭代之后, D_{k}\[i,j\] 包含从顶点 i 到顶点 j 且不经过任何编号大于 k 的顶点的最短路径长度值
例子:

D0: (k=0)
1 |
2 |
3 |
|
|---|---|---|---|
1 |
0 | 2 | 9 |
2 |
8 | 0 | 6 |
3 |
1 | ∞ \infty ∞ | 0 |
D1: (k=i=j=1)
1 |
2 |
3 |
|
|---|---|---|---|
1 |
0 | 2 | 9 |
2 |
8 | 0 | |
3 |
1 | 0 |
D1:
( D 1 [ 2 , 3 ] = m i n { D 0 [ 2 , 3 ] , D 0 [ 2 , 1 ] + D 0 [ 1 , 3 ] } = 6 D_1[2,3]=min\{D_0[2,3],D_0[2,1]+D_0[1,3]\}=6 D1[2,3]=min{D0[2,3],D0[2,1]+D0[1,3]}=6)
( D 1 [ 3 , 2 ] = m i n { D 0 [ 3 , 2 ] , D 0 [ 3 , 1 ] + D 0 [ 1 , 2 ] } = 3 D_1[3,2]=min\{D_0[3,2],D_0[3,1]+D_0[1,2]\}=3 D1[3,2]=min{D0[3,2],D0[3,1]+D0[1,2]}=3)
1 |
2 |
3 |
|
|---|---|---|---|
1 |
0 | 2 | 9 |
2 |
8 | 0 | 6 |
3 |
1 | 3 | 0 |
D2: (k=i=j=2)
1 |
2 |
3 |
|
|---|---|---|---|
1 |
0 | 2 | |
2 |
8 | 0 | 6 |
3 |
3 | 0 |
D2:
( D 2 [ 1 , 3 ] = m i n { D 1 [ 1 , 3 ] , D 1 [ 1 , 2 ] + D 1 [ 2 , 3 ] } = 8 D_2[1,3]=min\{D_1[1,3],D_1[1,2]+D_1[2,3]\}=8 D2[1,3]=min{D1[1,3],D1[1,2]+D1[2,3]}=8)
( D 2 [ 3 , 1 ] = m i n { D 1 [ 3 , 1 ] , D 1 [ 3 , 2 ] + D 1 [ 2 , 1 ] } = 1 D_2[3,1]=min\{D_1[3,1],D_1[3,2]+D_1[2,1]\}=1 D2[3,1]=min{D1[3,1],D1[3,2]+D1[2,1]}=1)
1 |
2 |
3 |
|
|---|---|---|---|
1 |
0 | 2 | 8 |
2 |
8 | 0 | 6 |
3 |
1 | 3 | 0 |
D3: (k=i=j=3)
1 |
2 |
3 |
|
|---|---|---|---|
1 |
0 | 8 | |
2 |
0 | 6 | |
3 |
1 | 3 | 0 |
D3:
( D 3 [ 1 , 2 ] = m i n { D 2 [ 1 , 2 ] , D 2 [ 1 , 3 ] + D 2 [ 3 , 2 ] } = 2 D_3[1,2]=min\{D_2[1,2],D_2[1,3]+D_2[3,2]\}=2 D3[1,2]=min{D2[1,2],D2[1,3]+D2[3,2]}=2)
( D 3 [ 2 , 1 ] = m i n { D 2 [ 2 , 1 ] , D 2 [ 2 , 3 ] + D 2 [ 3 , 1 ] } = 7 D_3[2,1]=min\{D_2[2,1],D_2[2,3]+D_2[3,1]\}=7 D3[2,1]=min{D2[2,1],D2[2,3]+D2[3,1]}=7)
1 |
2 |
3 |
|
|---|---|---|---|
1 |
0 | 2 | 8 |
2 |
7 | 0 | 6 |
3 |
1 | 3 | 0 |
3. 0/1背包问题Knapsack
设 U = { u 1 , u 2 , ... , u n } U = \{ u_{1}, u_{2}, \dots, u_{n} \} U={u1,u2,...,un} 为待装入容量为 C C C 的背包中的 n n n 个物品的集合
- 对于 1 ≤ j ≤ n 1 \leq j \leq n 1≤j≤n,令 s j s_{j} sj 和 v j v_{j} vj 分别表示第 j j j 件物品的体积和价值
(均为正整数)
目标:从 U U U 中选取若干物品装入背包,使得总体积不超过 C C C,且总价值最大
-> 用数学表达,即求一个向量 ( x 1 , x 2 , ... , x n ) ∈ { 0 , 1 } n (x_1, x_2, \dots, x_n) \in \{0, 1\}^n (x1,x2,...,xn)∈{0,1}n,使得:
最大化 ∑ j = 1 n v j x j 满足约束 ∑ j = 1 n s j x j ≤ C \text{最大化} \quad \sum_{j=1}^{n} v_{j} x_{j} \\ \text{满足约束} \quad \sum_{j=1}^{n} s_{j} x_{j} \leq C 最大化j=1∑nvjxj满足约束j=1∑nsjxj≤C
简言之,找到子集 S ⊆ U S \subseteq U S⊆U,使得 ∑ u i ∈ S v i \sum_{u_{i} \in S} v_{i} ∑ui∈Svi最大化,且 ∑ u i ∈ S s i ≤ C \sum_{u_{i} \in S} s_{i} \leq C ∑ui∈Ssi≤C
物品要么选要么不选,不能分割 -> 0/1背包问题
动态规划:
设 V [ i , j ] V[i, j] V[i,j] 表示使用前 i i i 个物品 { u 1 , u 2 , ... , u i } \{u_1, u_2, \dots, u_i\} {u1,u2,...,ui} 以最优方式填充大小为 j j j 的背包所能获得的最大价值( 0 ≤ i ≤ n , 0 ≤ j ≤ C 0 \leq i \leq n, 0 \leq j \leq C 0≤i≤n,0≤j≤C)
-> 最终要求解的值是 V [ n , C ] V[n, C] V[n,C]
- 对于所有的 j j j, V [ 0 , j ] = 0 V[0, j] = 0 V[0,j]=0(没有物品时,任何容量背包的最大价值为0)
- 对于所有的 i i i, V [ i , 0 ] = 0 V[i, 0] = 0 V[i,0]=0(容量为0的背包无法放入任何物品,价值为0)
递推关系:
V [ i , j ] = { 0 if i = 0 or j = 0 V [ i − 1 , j ] if s i > j max { V [ i − 1 , j ] , v i + V [ i − 1 , j − s i ] } if s i ≤ j V[i,j] = \begin{cases} 0 & \text{if } i=0 \text{ or } j=0 \\ V[i-1,j] & \text{if } s_i>j \\ \max \left\{ V[i-1,j],v_i+V[i-1,j-s_i] \right\} & \text{if } s_i \leq j \end{cases} V[i,j]=⎩ ⎨ ⎧0V[i−1,j]max{V[i−1,j],vi+V[i−1,j−si]}if i=0 or j=0if si>jif si≤j
时间复杂度: Θ ( n C ) \Theta(nC) Θ(nC)
空间复杂度: Θ ( C ) \Theta(C) Θ(C)
例子:

| v i v_i vi | s i s_i si | KaTeX parse error: Undefined control sequence: \textbackslash at position 3: i \̲t̲e̲x̲t̲b̲a̲c̲k̲s̲l̲a̲s̲h̲ ̲j | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ||
| 3 | 2 | 1 | 0 | 0 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
| 4 | 3 | 2 | 0 | 0 | 3 | 4 | 4 | 7 | 7 | 7 | 7 | 7 |
| 5 | 4 | 3 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 9 | 12 |
| 7 | 5 | 4 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 10 | 11 | 14(结果) |