动态规划
什么叫动态规划
动态规划(DP,Dynamic Programming)是一种解决问题的方法,尤其使用于处理哪些可以分解成相互关联小问题的情况。核心思想是避免重复计算,通过保存已解决的子问题答案,逐步解决更大规模问题的最优解。
案例解释
想象你在爬楼梯,楼梯有n阶台阶,每次你可以跨1步或2步。你想知道有多少种不同的方式可以爬到顶层???
我们不用重复数遍所有可能的走法,而是先算出走到前几步有多少种方式,然后利用这些结果组合得出到达更高阶的步数。
比如,先算出走到第1阶和第2阶各有几种方法,然后结合这两个结果得出到第3阶的方法数,以此类推,直至最终目标。
假定10阶台阶分析:
到第10阶台阶前是在第8阶或第九阶:f(10) = f(8) + f(9)
动态规划三要素
- 最有子结构: f(x-1), f(x-2)
- 状态转移方程:f(x)=f(x-1)+f(x-2)
- 边界值:f(1)=1, f(2)=2
解决问题代码如下:
1 | int getWays(int n) |
时间复杂服O(2^N),可以使用空间换时间的方法来降低时间复杂度。使用遍历的方式而不用递归。
难度升级
背包问题:
思考下如何转化三要素:
问题转化:
当前f(5,4) :
- 将物品4放入:6+f(1,3)
- 不放入物品4: f(5,3)
存在两种状态:比较大小max(6+f(1,3), f(5,3))
边界值f(0, n) = 0, f(n, 1) = 最重的一个物品 = 3
根据上边的分析可以得到:
- 转态转移方程:f(w,i) = max(wi+f(w-wi, i-1), f(w,i-1))
- 最右子结构:f(w-wi, i-1), f(w,i-1)
- 边界值:f(0, n) = 0, f(n, 1) = max(wi)
求解表格中的右下角数据:
使用python解决上述问题代码如下:
1 | def knapsack_dp(weights, values, capacity): |
代码核心公式:
f(capacity, len(weights)) =
max
(
weights[len(weights)]+f(capacity-weights[len(weights)], len(weights)-1),
f(capacity, len(weights)-1)
)