This commit is contained in:
krahets
2023-07-16 04:18:52 +08:00
parent c342ba3ced
commit edcd1e5c10
21 changed files with 475 additions and 67 deletions
@@ -2,14 +2,14 @@
comments: true
---
# 13.3.   动态规划解题思路
# 14.3.   动态规划解题思路
上两节介绍了动态规划问题的主要特征,接下来我们一起探究两个更加实用的问题:
1. 如何判断一个问题是不是动态规划问题?
2. 求解动态规划问题该从何处入手,完整步骤是什么?
## 13.3.1.   问题判断
## 14.3.1.   问题判断
总的来说,如果一个问题包含重叠子问题、最优子结构,并满足无后效性,那么它通常就适合用动态规划求解,但我们很难从问题描述上直接提取出这些特性。因此我们通常会放宽条件,**先观察问题是否适合使用回溯(穷举)解决**。
@@ -29,7 +29,7 @@ comments: true
如果一个问题满足决策树模型,并具有较为明显的“加分项“,我们就可以假设它是一个动态规划问题,并尝试求解它。
## 13.3.2.   问题求解
## 14.3.2.   问题求解
动态规划的解题流程可能会因问题的性质和难度而有所不同,但通常遵循以下步骤:描述决策,定义状态,建立 $dp$ 表,推导状态转移方程,确定边界条件等。
@@ -99,7 +99,7 @@ $$
接下来,我们就可以实现动态规划代码了。然而,由于子问题分解是一种从顶至底的思想,因此按照“暴力搜索 $\rightarrow$ 记忆化搜索 $\rightarrow$ 动态规划”的顺序实现更加符合思维习惯。
## 13.3.3.   方法一:暴力搜索
## 14.3.3.   方法一:暴力搜索
从状态 $[i, j]$ 开始搜索,不断分解为更小的状态 $[i-1, j]$ 和 $[i, j-1]$ ,包括以下递归要素:
@@ -240,7 +240,7 @@ $$
每个状态都有向下和向右两种选择,从左上角走到右下角总共需要 $m + n - 2$ 步,所以最差时间复杂度为 $O(2^{m + n})$ 。请注意,这种计算方式未考虑临近网格边界的情况,当到达网络边界时只剩下一种选择。因此实际的路径数量会少一些。
## 13.3.4.   方法二:记忆化搜索
## 14.3.4.   方法二:记忆化搜索
为了避免重复计算重叠子问题,我们引入一个和网格 `grid` 相同尺寸的记忆列表 `mem` ,用于记录各个子问题的解,提升搜索效率。
@@ -394,7 +394,7 @@ $$
<p align="center"> Fig. 记忆化搜索递归树 </p>
## 13.3.5. &nbsp; 方法三:动态规划
## 14.3.5. &nbsp; 方法三:动态规划
动态规划代码是从底至顶的,仅需循环即可实现。