This commit is contained in:
krahets
2023-08-19 22:07:27 +08:00
parent 71c7786f51
commit 2e27ad1680
99 changed files with 283 additions and 283 deletions

View File

@@ -3,7 +3,7 @@ comments: true
status: new
---
# 14.2.   动态规划问题特性
# 14.2   动态规划问题特性
在上节中,我们学习了动态规划是如何通过子问题分解来求解问题的。实际上,子问题分解是一种通用的算法思路,在分治、动态规划、回溯中的侧重点不同:
@@ -13,7 +13,7 @@ status: new
实际上,动态规划常用来求解最优化问题,它们不仅包含重叠子问题,还具有另外两大特性:最优子结构、无后效性。
## 14.2.1.   最优子结构
## 14.2.1   最优子结构
我们对爬楼梯问题稍作改动,使之更加适合展示最优子结构概念。
@@ -431,7 +431,7 @@ $$
}
```
## 14.2.2.   无后效性
## 14.2.2   无后效性
「无后效性」是动态规划能够有效解决问题的重要特性之一,定义为:**给定一个确定的状态,它的未来发展只与当前状态有关,而与当前状态过去所经历过的所有状态无关**。

View File

@@ -3,14 +3,14 @@ comments: true
status: new
---
# 14.3.   动态规划解题思路
# 14.3   动态规划解题思路
上两节介绍了动态规划问题的主要特征,接下来我们一起探究两个更加实用的问题:
1. 如何判断一个问题是不是动态规划问题?
2. 求解动态规划问题该从何处入手,完整步骤是什么?
## 14.3.1.   问题判断
## 14.3.1   问题判断
总的来说,如果一个问题包含重叠子问题、最优子结构,并满足无后效性,那么它通常就适合用动态规划求解。然而,我们很难从问题描述上直接提取出这些特性。因此我们通常会放宽条件,**先观察问题是否适合使用回溯(穷举)解决**。
@@ -30,7 +30,7 @@ status: new
如果一个问题满足决策树模型,并具有较为明显的“加分项“,我们就可以假设它是一个动态规划问题,并在求解过程中验证它。
## 14.3.2.   问题求解步骤
## 14.3.2   问题求解步骤
动态规划的解题流程会因问题的性质和难度而有所不同,但通常遵循以下步骤:描述决策,定义状态,建立 $dp$ 表,推导状态转移方程,确定边界条件等。

View File

@@ -3,7 +3,7 @@ comments: true
status: new
---
# 14.6.   编辑距离问题
# 14.6   编辑距离问题
编辑距离,也被称为 Levenshtein 距离,指两个字符串之间互相转换的最小修改次数,通常用于在信息检索和自然语言处理中度量两个序列的相似度。

View File

@@ -4,7 +4,7 @@ icon: material/table-pivot
status: new
---
# 14.   动态规划
# 第 14 章   动态规划
<div class="center-table" markdown>

View File

@@ -3,7 +3,7 @@ comments: true
status: new
---
# 14.1. &nbsp; 初探动态规划
# 14.1 &nbsp; 初探动态规划
「动态规划 Dynamic Programming」是一个重要的算法范式它将一个问题分解为一系列更小的子问题并通过存储子问题的解来避免重复计算从而大幅提升时间效率。
@@ -360,7 +360,7 @@ status: new
}
```
## 14.1.1. &nbsp; 方法一:暴力搜索
## 14.1.1 &nbsp; 方法一:暴力搜索
回溯算法通常并不显式地对问题进行拆解,而是将问题看作一系列决策步骤,通过试探和剪枝,搜索所有可能的解。
@@ -615,7 +615,7 @@ $$
以此类推,子问题中包含更小的重叠子问题,子子孙孙无穷尽也。绝大部分计算资源都浪费在这些重叠的问题上。
## 14.1.2. &nbsp; 方法二:记忆化搜索
## 14.1.2 &nbsp; 方法二:记忆化搜索
为了提升算法效率,**我们希望所有的重叠子问题都只被计算一次**。为此,我们声明一个数组 `mem` 来记录每个子问题的解,并在搜索过程中这样做:
@@ -923,7 +923,7 @@ $$
<p align="center"> 图:记忆化搜索对应递归树 </p>
## 14.1.3. &nbsp; 方法三:动态规划
## 14.1.3 &nbsp; 方法三:动态规划
**记忆化搜索是一种“从顶至底”的方法**:我们从原问题(根节点)开始,递归地将较大子问题分解为较小子问题,直至解已知的最小子问题(叶节点)。之后,通过回溯将子问题的解逐层收集,构建出原问题的解。
@@ -1167,7 +1167,7 @@ $$
<p align="center"> 图:爬楼梯的动态规划过程 </p>
## 14.1.4. &nbsp; 状态压缩
## 14.1.4 &nbsp; 状态压缩
细心的你可能发现,**由于 $dp[i]$ 只与 $dp[i-1]$ 和 $dp[i-2]$ 有关,因此我们无需使用一个数组 `dp` 来存储所有子问题的解**,而只需两个变量滚动前进即可。

View File

@@ -3,7 +3,7 @@ comments: true
status: new
---
# 14.4. &nbsp; 0-1 背包问题
# 14.4 &nbsp; 0-1 背包问题
背包问题是一个非常好的动态规划入门题目,是动态规划中最常见的问题形式。其具有很多变种,例如 0-1 背包问题、完全背包问题、多重背包问题等。

View File

@@ -3,7 +3,7 @@ comments: true
status: new
---
# 14.7. &nbsp; 小结
# 14.7 &nbsp; 小结
- 动态规划对问题进行分解,并通过存储子问题的解来规避重复计算,实现高效的计算效率。
- 不考虑时间的前提下,所有动态规划问题都可以用回溯(暴力搜索)进行求解,但递归树中存在大量的重叠子问题,效率极低。通过引入记忆化列表,可以存储所有计算过的子问题的解,从而保证重叠子问题只被计算一次。

View File

@@ -3,11 +3,11 @@ comments: true
status: new
---
# 14.5. &nbsp; 完全背包问题
# 14.5 &nbsp; 完全背包问题
在本节中,我们先求解另一个常见的背包问题:完全背包,再了解它的一种特例:零钱兑换。
## 14.5.1. &nbsp; 完全背包
## 14.5.1 &nbsp; 完全背包
!!! question
@@ -529,7 +529,7 @@ $$
}
```
## 14.5.2. &nbsp; 零钱兑换问题
## 14.5.2 &nbsp; 零钱兑换问题
背包问题是一大类动态规划问题的代表,其拥有很多的变种,例如零钱兑换问题。
@@ -1178,7 +1178,7 @@ $$
}
```
## 14.5.3. &nbsp; 零钱兑换问题 II
## 14.5.3 &nbsp; 零钱兑换问题 II
!!! question