[算法设计与分析-从入门到入土] 动态规划

[算法设计与分析-从入门到入土] 动态规划

知乎: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

  1. i = j i=j i=j时, d i , j k = 0 d_{i,j}^k=0 di,jk=0(顶点到自身的距离为0)
  2. 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(结果)
相关推荐
君义_noip20 小时前
信息学奥赛一本通 1528:【例 2】单词游戏
c++·算法·信息学奥赛·一本通·csp-s
AlenTech20 小时前
238. 除了自身以外数组的乘积 - 力扣(LeetCode)
算法·leetcode·职场和发展
l1t20 小时前
利用豆包辅助编写数独隐式唯一数填充c程序
c语言·开发语言·人工智能·算法·豆包·deepseek
cici1587420 小时前
matlab实现NSGA-II(带精英策略的非支配排序遗传算法)
算法
乐迪信息20 小时前
乐迪信息:智能识别船舶种类的AI解决方案
大数据·网络·人工智能·算法·无人机
朔北之忘 Clancy21 小时前
第一章 顺序结构程序设计(1)
c++·算法·青少年编程·竞赛·教材·考级·讲义
老鼠只爱大米21 小时前
LeetCode经典算法面试题 #41:缺失的第一个正数(位置交换法、标记法等多种方法详解)
算法·leetcode·原地哈希·缺失的第一个正数·算法面试·位图法·集合哈希法
hetao173383721 小时前
2026-01-14~15 hetao1733837 的刷题笔记
c++·笔记·算法
百度搜不到…21 小时前
背包问题递推公式中的dp[j-nums[j]]到底怎么理解
算法·leetcode·动态规划·背包问题
一起养小猫21 小时前
LeetCode100天Day13-移除元素与多数元素
java·算法·leetcode