算法介绍
策略
将原问题递归地分解成子问题,之后再由所有子问题的解构造出最终解。
与分治法的区别
- 分治法将原问题分解成多个子问题,独立地求解每个子问题,并对所有解进行组合获得最终解,比如快速排序
- 动态规划同样做了分解原问题的步骤,但是分解获得的子问题是叠加的,这意味着动态规划更适用于“原问题可分解为叠加的子问题”的情况。
使用条件
- 最优子结构(贪心章节有讲到)
- 无后效性
存在状态转移方程,使得转移后的状态不需要考虑转移前的状态。 - 子问题重叠性质
子问题间不独立
一般步骤
1.将问题分解成叠加的子问题
2.递归地求解子问题
3.由子问题的解构造最终解
(至此讲的确实抽象,写使用条件以及步骤是为了突出该算法的特征,如果上来就给个例子,后面复习时可能需要通过思考例子来重复地提取出算法的骨架,影响效率)
举例
0-1背包问题
问题描述
现有1个背包容量为C,有N个物品,其中第 i 个物品重量为
W
i
W_i
Wi,价值为
V
i
V_i
Vi。问如何装使得总体价值最高。
问题分析
首先这个问题规模是有限的,可以遍历所有解,因此可以用蛮力法求解,时间复杂度为
O
(
2
n
)
O(2^n)
O(2n),要考虑更优解法;具有最优子结构,考虑贪心法,每一次选择都会减少背包的剩余容量,对下一个选择有影响,故没有贪心选择性质,推断贪心法不能获得最优解;具有无后效性和子问题重叠性质,考虑动态规划,条件满足,尝试求解。
问题求解
设
d
p
[
i
]
[
C
]
dp[i][C]
dp[i][C]表示在容量为
C
C
C的背包中放入前
i
i
i件物品获得的最大价值,
则动态规划状态转移方程如下
d
p
[
i
]
[
C
]
=
m
a
x
(
d
p
[
i
−
1
]
[
C
]
,
d
p
[
i
−
1
]
[
C
−
W
i
]
+
V
i
)
dp[i][C]=max(dp[i-1][C],dp[i-1][C-W_i]+V_i)
dp[i][C]=max(dp[i−1][C],dp[i−1][C−Wi]+Vi)其中,等式右边的前半部分表示不放入第
i
i
i件物品,后半部分表示放入第
i
i
i件物品。
程序示例(python)
懒得写 :/,用python写的话关键代码和状态转移方程形式基本一样。
思考
- 动态规划为什么在解决一些问题上更加优秀?
从直观上理解,动态规划方法在计算过程中进行了“记录”,从而避免了部分冗余计算。举个例子,计算 1 + 1 + ⋯ + 1 1+1+\cdots+1 1+1+⋯+1,如果使用顺序计算的策略,就会计算 1 + 1 , 1 + 1 + 1 , 1 + 1 + 1 , ⋯ 1+1,1+1+1,1+1+1,\cdots 1+1,1+1+1,1+1+1,⋯,这个过程中进行了多次冗余计算。