mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-10 22:46:07 +00:00
build
This commit is contained in:
@@ -21,11 +21,11 @@ status: new
|
||||
|
||||
给定一个楼梯,你每步可以上 $1$ 阶或者 $2$ 阶,每一阶楼梯上都贴有一个非负整数,表示你在该台阶所需要付出的代价。给定一个非负整数数组 $cost$ ,其中 $cost[i]$ 表示在第 $i$ 个台阶需要付出的代价,$cost[0]$ 为地面起始点。请计算最少需要付出多少代价才能到达顶部?
|
||||
|
||||
如下图所示,若第 $1$ , $2$ , $3$ 阶的代价分别为 $1$ , $10$ , $1$ ,则从地面爬到第 $3$ 阶的最小代价为 $2$ 。
|
||||
如图 14-6 所示,若第 $1$ , $2$ , $3$ 阶的代价分别为 $1$ , $10$ , $1$ ,则从地面爬到第 $3$ 阶的最小代价为 $2$ 。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:爬到第 3 阶的最小代价 </p>
|
||||
<p align="center"> 图 14-6 爬到第 3 阶的最小代价 </p>
|
||||
|
||||
设 $dp[i]$ 为爬到第 $i$ 阶累计付出的代价,由于第 $i$ 阶只可能从 $i - 1$ 阶或 $i - 2$ 阶走来,因此 $dp[i]$ 只可能等于 $dp[i - 1] + cost[i]$ 或 $dp[i - 2] + cost[i]$ 。为了尽可能减少代价,我们应该选择两者中较小的那一个,即:
|
||||
|
||||
@@ -246,11 +246,11 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
下图展示了以上代码的动态规划过程。
|
||||
图 14-7 展示了以上代码的动态规划过程。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:爬楼梯最小代价的动态规划过程 </p>
|
||||
<p align="center"> 图 14-7 爬楼梯最小代价的动态规划过程 </p>
|
||||
|
||||
本题也可以进行状态压缩,将一维压缩至零维,使得空间复杂度从 $O(n)$ 降低至 $O(1)$ 。
|
||||
|
||||
@@ -445,11 +445,11 @@ $$
|
||||
|
||||
给定一个共有 $n$ 阶的楼梯,你每步可以上 $1$ 阶或者 $2$ 阶,**但不能连续两轮跳 $1$ 阶**,请问有多少种方案可以爬到楼顶。
|
||||
|
||||
例如下图,爬上第 $3$ 阶仅剩 $2$ 种可行方案,其中连续三次跳 $1$ 阶的方案不满足约束条件,因此被舍弃。
|
||||
例如图 14-8 ,爬上第 $3$ 阶仅剩 $2$ 种可行方案,其中连续三次跳 $1$ 阶的方案不满足约束条件,因此被舍弃。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:带约束爬到第 3 阶的方案数量 </p>
|
||||
<p align="center"> 图 14-8 带约束爬到第 3 阶的方案数量 </p>
|
||||
|
||||
在该问题中,如果上一轮是跳 $1$ 阶上来的,那么下一轮就必须跳 $2$ 阶。这意味着,**下一步选择不能由当前状态(当前楼梯阶数)独立决定,还和前一个状态(上轮楼梯阶数)有关**。
|
||||
|
||||
@@ -460,7 +460,7 @@ $$
|
||||
- 当 $j$ 等于 $1$ ,即上一轮跳了 $1$ 阶时,这一轮只能选择跳 $2$ 阶。
|
||||
- 当 $j$ 等于 $2$ ,即上一轮跳了 $2$ 阶时,这一轮可选择跳 $1$ 阶或跳 $2$ 阶。
|
||||
|
||||
如下图所示,在该定义下,$dp[i, j]$ 表示状态 $[i, j]$ 对应的方案数。此时状态转移方程为:
|
||||
如图 14-9 所示,在该定义下,$dp[i, j]$ 表示状态 $[i, j]$ 对应的方案数。此时状态转移方程为:
|
||||
|
||||
$$
|
||||
\begin{cases}
|
||||
@@ -471,7 +471,7 @@ $$
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:考虑约束下的递推关系 </p>
|
||||
<p align="center"> 图 14-9 考虑约束下的递推关系 </p>
|
||||
|
||||
最终,返回 $dp[n, 1] + dp[n, 2]$ 即可,两者之和代表爬到第 $n$ 阶的方案总数。
|
||||
|
||||
|
||||
@@ -40,11 +40,11 @@ status: new
|
||||
|
||||
给定一个 $n \times m$ 的二维网格 `grid` ,网格中的每个单元格包含一个非负整数,表示该单元格的代价。机器人以左上角单元格为起始点,每次只能向下或者向右移动一步,直至到达右下角单元格。请返回从左上角到右下角的最小路径和。
|
||||
|
||||
下图展示了一个例子,给定网格的最小路径和为 $13$ 。
|
||||
图 14-10 展示了一个例子,给定网格的最小路径和为 $13$ 。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:最小路径和示例数据 </p>
|
||||
<p align="center"> 图 14-10 最小路径和示例数据 </p>
|
||||
|
||||
**第一步:思考每轮的决策,定义状态,从而得到 $dp$ 表**
|
||||
|
||||
@@ -52,11 +52,11 @@ status: new
|
||||
|
||||
状态 $[i, j]$ 对应的子问题为:从起始点 $[0, 0]$ 走到 $[i, j]$ 的最小路径和,解记为 $dp[i, j]$ 。
|
||||
|
||||
至此,我们就得到了下图所示的二维 $dp$ 矩阵,其尺寸与输入网格 $grid$ 相同。
|
||||
至此,我们就得到了图 14-11 所示的二维 $dp$ 矩阵,其尺寸与输入网格 $grid$ 相同。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:状态定义与 dp 表 </p>
|
||||
<p align="center"> 图 14-11 状态定义与 dp 表 </p>
|
||||
|
||||
!!! note
|
||||
|
||||
@@ -68,7 +68,7 @@ status: new
|
||||
|
||||
对于状态 $[i, j]$ ,它只能从上边格子 $[i-1, j]$ 和左边格子 $[i, j-1]$ 转移而来。因此最优子结构为:到达 $[i, j]$ 的最小路径和由 $[i, j-1]$ 的最小路径和与 $[i-1, j]$ 的最小路径和,这两者较小的那一个决定。
|
||||
|
||||
根据以上分析,可推出下图所示的状态转移方程:
|
||||
根据以上分析,可推出图 14-12 所示的状态转移方程:
|
||||
|
||||
$$
|
||||
dp[i, j] = \min(dp[i-1, j], dp[i, j-1]) + grid[i, j]
|
||||
@@ -76,7 +76,7 @@ $$
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:最优子结构与状态转移方程 </p>
|
||||
<p align="center"> 图 14-12 最优子结构与状态转移方程 </p>
|
||||
|
||||
!!! note
|
||||
|
||||
@@ -88,11 +88,11 @@ $$
|
||||
|
||||
在本题中,处在首行的状态只能向右转移,首列状态只能向下转移,因此首行 $i = 0$ 和首列 $j = 0$ 是边界条件。
|
||||
|
||||
如下图所示,由于每个格子是由其左方格子和上方格子转移而来,因此我们使用采用循环来遍历矩阵,外循环遍历各行、内循环遍历各列。
|
||||
如图 14-13 所示,由于每个格子是由其左方格子和上方格子转移而来,因此我们使用采用循环来遍历矩阵,外循环遍历各行、内循环遍历各列。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:边界条件与状态转移顺序 </p>
|
||||
<p align="center"> 图 14-13 边界条件与状态转移顺序 </p>
|
||||
|
||||
!!! note
|
||||
|
||||
@@ -316,13 +316,13 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
下图给出了以 $dp[2, 1]$ 为根节点的递归树,其中包含一些重叠子问题,其数量会随着网格 `grid` 的尺寸变大而急剧增多。
|
||||
图 14-14 给出了以 $dp[2, 1]$ 为根节点的递归树,其中包含一些重叠子问题,其数量会随着网格 `grid` 的尺寸变大而急剧增多。
|
||||
|
||||
本质上看,造成重叠子问题的原因为:**存在多条路径可以从左上角到达某一单元格**。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:暴力搜索递归树 </p>
|
||||
<p align="center"> 图 14-14 暴力搜索递归树 </p>
|
||||
|
||||
每个状态都有向下和向右两种选择,从左上角走到右下角总共需要 $m + n - 2$ 步,所以最差时间复杂度为 $O(2^{m + n})$ 。请注意,这种计算方式未考虑临近网格边界的情况,当到达网络边界时只剩下一种选择。因此实际的路径数量会少一些。
|
||||
|
||||
@@ -582,11 +582,11 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
如下图所示,在引入记忆化后,所有子问题的解只需计算一次,因此时间复杂度取决于状态总数,即网格尺寸 $O(nm)$ 。
|
||||
如图 14-15 所示,在引入记忆化后,所有子问题的解只需计算一次,因此时间复杂度取决于状态总数,即网格尺寸 $O(nm)$ 。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:记忆化搜索递归树 </p>
|
||||
<p align="center"> 图 14-15 记忆化搜索递归树 </p>
|
||||
|
||||
### 3. 方法三:动态规划
|
||||
|
||||
@@ -853,7 +853,7 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
下图展示了最小路径和的状态转移过程,其遍历了整个网格,**因此时间复杂度为 $O(nm)$** 。
|
||||
图 14-16 展示了最小路径和的状态转移过程,其遍历了整个网格,**因此时间复杂度为 $O(nm)$** 。
|
||||
|
||||
数组 `dp` 大小为 $n \times m$ ,**因此空间复杂度为 $O(nm)$** 。
|
||||
|
||||
@@ -893,7 +893,7 @@ $$
|
||||
=== "<12>"
|
||||

|
||||
|
||||
<p align="center"> 图:最小路径和的动态规划过程 </p>
|
||||
<p align="center"> 图 14-16 最小路径和的动态规划过程 </p>
|
||||
|
||||
### 4. 状态压缩
|
||||
|
||||
|
||||
@@ -13,21 +13,21 @@ status: new
|
||||
|
||||
你可以在一个字符串中进行三种编辑操作:插入一个字符、删除一个字符、替换字符为任意一个字符。
|
||||
|
||||
如下图所示,将 `kitten` 转换为 `sitting` 需要编辑 3 步,包括 2 次替换操作与 1 次添加操作;将 `hello` 转换为 `algo` 需要 3 步,包括 2 次替换操作和 1 次删除操作。
|
||||
如图 14-27 所示,将 `kitten` 转换为 `sitting` 需要编辑 3 步,包括 2 次替换操作与 1 次添加操作;将 `hello` 转换为 `algo` 需要 3 步,包括 2 次替换操作和 1 次删除操作。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:编辑距离的示例数据 </p>
|
||||
<p align="center"> 图 14-27 编辑距离的示例数据 </p>
|
||||
|
||||
**编辑距离问题可以很自然地用决策树模型来解释**。字符串对应树节点,一轮决策(一次编辑操作)对应树的一条边。
|
||||
|
||||
如下图所示,在不限制操作的情况下,每个节点都可以派生出许多条边,每条边对应一种操作,这意味着从 `hello` 转换到 `algo` 有许多种可能的路径。
|
||||
如图 14-28 所示,在不限制操作的情况下,每个节点都可以派生出许多条边,每条边对应一种操作,这意味着从 `hello` 转换到 `algo` 有许多种可能的路径。
|
||||
|
||||
从决策树的角度看,本题的目标是求解节点 `hello` 和节点 `algo` 之间的最短路径。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:基于决策树模型表示编辑距离问题 </p>
|
||||
<p align="center"> 图 14-28 基于决策树模型表示编辑距离问题 </p>
|
||||
|
||||
### 1. 动态规划思路
|
||||
|
||||
@@ -48,7 +48,7 @@ status: new
|
||||
|
||||
**第二步:找出最优子结构,进而推导出状态转移方程**
|
||||
|
||||
考虑子问题 $dp[i, j]$ ,其对应的两个字符串的尾部字符为 $s[i-1]$ 和 $t[j-1]$ ,可根据不同编辑操作分为下图所示的三种情况:
|
||||
考虑子问题 $dp[i, j]$ ,其对应的两个字符串的尾部字符为 $s[i-1]$ 和 $t[j-1]$ ,可根据不同编辑操作分为图 14-29 所示的三种情况:
|
||||
|
||||
1. 在 $s[i-1]$ 之后添加 $t[j-1]$ ,则剩余子问题 $dp[i, j-1]$ 。
|
||||
2. 删除 $s[i-1]$ ,则剩余子问题 $dp[i-1, j]$ 。
|
||||
@@ -56,7 +56,7 @@ status: new
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:编辑距离的状态转移 </p>
|
||||
<p align="center"> 图 14-29 编辑距离的状态转移 </p>
|
||||
|
||||
根据以上分析,可得最优子结构:$dp[i, j]$ 的最少编辑步数等于 $dp[i, j-1]$ , $dp[i-1, j]$ , $dp[i-1, j-1]$ 三者中的最少编辑步数,再加上本次的编辑步数 $1$ 。对应的状态转移方程为:
|
||||
|
||||
@@ -366,7 +366,7 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
如下图所示,编辑距离问题的状态转移过程与背包问题非常类似,都可以看作是填写一个二维网格的过程。
|
||||
如图 14-30 所示,编辑距离问题的状态转移过程与背包问题非常类似,都可以看作是填写一个二维网格的过程。
|
||||
|
||||
=== "<1>"
|
||||

|
||||
@@ -413,7 +413,7 @@ $$
|
||||
=== "<15>"
|
||||

|
||||
|
||||
<p align="center"> 图:编辑距离的动态规划过程 </p>
|
||||
<p align="center"> 图 14-30 编辑距离的动态规划过程 </p>
|
||||
|
||||
### 3. 状态压缩
|
||||
|
||||
|
||||
@@ -13,11 +13,11 @@ status: new
|
||||
|
||||
给定一个共有 $n$ 阶的楼梯,你每步可以上 $1$ 阶或者 $2$ 阶,请问有多少种方案可以爬到楼顶。
|
||||
|
||||
如下图所示,对于一个 $3$ 阶楼梯,共有 $3$ 种方案可以爬到楼顶。
|
||||
如图 14-1 所示,对于一个 $3$ 阶楼梯,共有 $3$ 种方案可以爬到楼顶。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:爬到第 3 阶的方案数量 </p>
|
||||
<p align="center"> 图 14-1 爬到第 3 阶的方案数量 </p>
|
||||
|
||||
本题的目标是求解方案数量,**我们可以考虑通过回溯来穷举所有可能性**。具体来说,将爬楼梯想象为一个多轮选择的过程:从地面出发,每轮选择上 $1$ 阶或 $2$ 阶,每当到达楼梯顶部时就将方案数量加 $1$ ,当越过楼梯顶部时就将其剪枝。
|
||||
|
||||
@@ -378,11 +378,11 @@ $$
|
||||
dp[i] = dp[i-1] + dp[i-2]
|
||||
$$
|
||||
|
||||
这意味着在爬楼梯问题中,各个子问题之间存在递推关系,**原问题的解可以由子问题的解构建得来**。下图展示了该递推关系。
|
||||
这意味着在爬楼梯问题中,各个子问题之间存在递推关系,**原问题的解可以由子问题的解构建得来**。图 14-2 展示了该递推关系。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:方案数量递推关系 </p>
|
||||
<p align="center"> 图 14-2 方案数量递推关系 </p>
|
||||
|
||||
我们可以根据递推公式得到暴力搜索解法:
|
||||
|
||||
@@ -605,13 +605,13 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
下图展示了暴力搜索形成的递归树。对于问题 $dp[n]$ ,其递归树的深度为 $n$ ,时间复杂度为 $O(2^n)$ 。指数阶属于爆炸式增长,如果我们输入一个比较大的 $n$ ,则会陷入漫长的等待之中。
|
||||
图 14-3 展示了暴力搜索形成的递归树。对于问题 $dp[n]$ ,其递归树的深度为 $n$ ,时间复杂度为 $O(2^n)$ 。指数阶属于爆炸式增长,如果我们输入一个比较大的 $n$ ,则会陷入漫长的等待之中。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:爬楼梯对应递归树 </p>
|
||||
<p align="center"> 图 14-3 爬楼梯对应递归树 </p>
|
||||
|
||||
观察上图发现,**指数阶的时间复杂度是由于“重叠子问题”导致的**。例如:$dp[9]$ 被分解为 $dp[8]$ 和 $dp[7]$ ,$dp[8]$ 被分解为 $dp[7]$ 和 $dp[6]$ ,两者都包含子问题 $dp[7]$ 。
|
||||
观察图 14-3 ,**指数阶的时间复杂度是由于“重叠子问题”导致的**。例如 $dp[9]$ 被分解为 $dp[8]$ 和 $dp[7]$ ,$dp[8]$ 被分解为 $dp[7]$ 和 $dp[6]$ ,两者都包含子问题 $dp[7]$ 。
|
||||
|
||||
以此类推,子问题中包含更小的重叠子问题,子子孙孙无穷尽也。绝大部分计算资源都浪费在这些重叠的问题上。
|
||||
|
||||
@@ -917,11 +917,11 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
观察下图,**经过记忆化处理后,所有重叠子问题都只需被计算一次,时间复杂度被优化至 $O(n)$** ,这是一个巨大的飞跃。
|
||||
观察图 14-4 ,**经过记忆化处理后,所有重叠子问题都只需被计算一次,时间复杂度被优化至 $O(n)$** ,这是一个巨大的飞跃。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:记忆化搜索对应递归树 </p>
|
||||
<p align="center"> 图 14-4 记忆化搜索对应递归树 </p>
|
||||
|
||||
## 14.1.3 方法三:动态规划
|
||||
|
||||
@@ -1155,11 +1155,11 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
下图模拟了以上代码的执行过程。
|
||||
图 14-5 模拟了以上代码的执行过程。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:爬楼梯的动态规划过程 </p>
|
||||
<p align="center"> 图 14-5 爬楼梯的动态规划过程 </p>
|
||||
|
||||
与回溯算法一样,动态规划也使用“状态”概念来表示问题求解的某个特定阶段,每个状态都对应一个子问题以及相应的局部最优解。例如,爬楼梯问题的状态定义为当前所在楼梯阶数 $i$ 。
|
||||
|
||||
|
||||
@@ -13,11 +13,11 @@ status: new
|
||||
|
||||
给定 $n$ 个物品,第 $i$ 个物品的重量为 $wgt[i-1]$ 、价值为 $val[i-1]$ ,和一个容量为 $cap$ 的背包。每个物品只能选择一次,问在不超过背包容量下能放入物品的最大价值。
|
||||
|
||||
观察下图,由于物品编号 $i$ 从 $1$ 开始计数,数组索引从 $0$ 开始计数,因此物品 $i$ 对应重量 $wgt[i-1]$ 和价值 $val[i-1]$ 。
|
||||
观察图 14-17 ,由于物品编号 $i$ 从 $1$ 开始计数,数组索引从 $0$ 开始计数,因此物品 $i$ 对应重量 $wgt[i-1]$ 和价值 $val[i-1]$ 。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:0-1 背包的示例数据 </p>
|
||||
<p align="center"> 图 14-17 0-1 背包的示例数据 </p>
|
||||
|
||||
我们可以将 0-1 背包问题看作是一个由 $n$ 轮决策组成的过程,每个物体都有不放入和放入两种决策,因此该问题是满足决策树模型的。
|
||||
|
||||
@@ -267,13 +267,13 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
如下图所示,由于每个物品都会产生不选和选两条搜索分支,因此时间复杂度为 $O(2^n)$ 。
|
||||
如图 14-18 所示,由于每个物品都会产生不选和选两条搜索分支,因此时间复杂度为 $O(2^n)$ 。
|
||||
|
||||
观察递归树,容易发现其中存在重叠子问题,例如 $dp[1, 10]$ 等。而当物品较多、背包容量较大,尤其是相同重量的物品较多时,重叠子问题的数量将会大幅增多。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:0-1 背包的暴力搜索递归树 </p>
|
||||
<p align="center"> 图 14-18 0-1 背包的暴力搜索递归树 </p>
|
||||
|
||||
### 2. 方法二:记忆化搜索
|
||||
|
||||
@@ -537,11 +537,11 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
下图展示了在记忆化递归中被剪掉的搜索分支。
|
||||
图 14-19 展示了在记忆化递归中被剪掉的搜索分支。
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:0-1 背包的记忆化搜索递归树 </p>
|
||||
<p align="center"> 图 14-19 0-1 背包的记忆化搜索递归树 </p>
|
||||
|
||||
### 3. 方法三:动态规划
|
||||
|
||||
@@ -780,7 +780,7 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
如下图所示,时间复杂度和空间复杂度都由数组 `dp` 大小决定,即 $O(n \times cap)$ 。
|
||||
如图 14-20 所示,时间复杂度和空间复杂度都由数组 `dp` 大小决定,即 $O(n \times cap)$ 。
|
||||
|
||||
=== "<1>"
|
||||

|
||||
@@ -824,7 +824,7 @@ $$
|
||||
=== "<14>"
|
||||

|
||||
|
||||
<p align="center"> 图:0-1 背包的动态规划过程 </p>
|
||||
<p align="center"> 图 14-20 0-1 背包的动态规划过程 </p>
|
||||
|
||||
### 4. 状态压缩
|
||||
|
||||
@@ -835,7 +835,7 @@ $$
|
||||
- 如果采取正序遍历,那么遍历到 $dp[i, j]$ 时,左上方 $dp[i-1, 1]$ ~ $dp[i-1, j-1]$ 值可能已经被覆盖,此时就无法得到正确的状态转移结果。
|
||||
- 如果采取倒序遍历,则不会发生覆盖问题,状态转移可以正确进行。
|
||||
|
||||
下图展示了在单个数组下从第 $i = 1$ 行转换至第 $i = 2$ 行的过程。请思考正序遍历和倒序遍历的区别。
|
||||
图 14-21 展示了在单个数组下从第 $i = 1$ 行转换至第 $i = 2$ 行的过程。请思考正序遍历和倒序遍历的区别。
|
||||
|
||||
=== "<1>"
|
||||

|
||||
@@ -855,7 +855,7 @@ $$
|
||||
=== "<6>"
|
||||

|
||||
|
||||
<p align="center"> 图:0-1 背包的状态压缩后的动态规划过程 </p>
|
||||
<p align="center"> 图 14-21 0-1 背包的状态压缩后的动态规划过程 </p>
|
||||
|
||||
在代码实现中,我们仅需将数组 `dp` 的第一维 $i$ 直接删除,并且把内循环更改为倒序遍历即可。
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ status: new
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:完全背包问题的示例数据 </p>
|
||||
<p align="center"> 图 14-22 完全背包问题的示例数据 </p>
|
||||
|
||||
### 1. 动态规划思路
|
||||
|
||||
@@ -276,7 +276,7 @@ $$
|
||||
|
||||
由于当前状态是从左边和上边的状态转移而来,**因此状态压缩后应该对 $dp$ 表中的每一行采取正序遍历**。
|
||||
|
||||
这个遍历顺序与 0-1 背包正好相反。请借助下图来理解两者的区别。
|
||||
这个遍历顺序与 0-1 背包正好相反。请借助图 14-23 来理解两者的区别。
|
||||
|
||||
=== "<1>"
|
||||

|
||||
@@ -296,7 +296,7 @@ $$
|
||||
=== "<6>"
|
||||

|
||||
|
||||
<p align="center"> 图:完全背包的状态压缩后的动态规划过程 </p>
|
||||
<p align="center"> 图 14-23 完全背包的状态压缩后的动态规划过程 </p>
|
||||
|
||||
代码实现比较简单,仅需将数组 `dp` 的第一维删除。
|
||||
|
||||
@@ -541,7 +541,7 @@ $$
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:零钱兑换问题的示例数据 </p>
|
||||
<p align="center"> 图 14-24 零钱兑换问题的示例数据 </p>
|
||||
|
||||
### 1. 动态规划思路
|
||||
|
||||
@@ -866,7 +866,7 @@ $$
|
||||
}
|
||||
```
|
||||
|
||||
下图展示了零钱兑换的动态规划过程,和完全背包非常相似。
|
||||
图 14-25 展示了零钱兑换的动态规划过程,和完全背包非常相似。
|
||||
|
||||
=== "<1>"
|
||||

|
||||
@@ -913,7 +913,7 @@ $$
|
||||
=== "<15>"
|
||||

|
||||
|
||||
<p align="center"> 图:零钱兑换问题的动态规划过程 </p>
|
||||
<p align="center"> 图 14-25 零钱兑换问题的动态规划过程 </p>
|
||||
|
||||
### 3. 状态压缩
|
||||
|
||||
@@ -1190,7 +1190,7 @@ $$
|
||||
|
||||

|
||||
|
||||
<p align="center"> 图:零钱兑换问题 II 的示例数据 </p>
|
||||
<p align="center"> 图 14-26 零钱兑换问题 II 的示例数据 </p>
|
||||
|
||||
### 1. 动态规划思路
|
||||
|
||||
|
||||
Reference in New Issue
Block a user