动态规划算法实践报告
1. 实践报告
1.1 根据最优子结构性质,列出递归方程式,说明方程式的定义、边界条件
(1)问题分析
本题要求商人从一个 N×N 网格的左上角出发,到达右下角,并且需要在 2N-1 个单位时间内完成。每经过一个格子都需要支付相应的通行费,要求计算最小的通行费总和。
由于从左上角到右下角至少需要向右移动 N-1 次、向下移动 N-1 次,加上起点格子,恰好经过 2N-1 个格子。因此,商人不能绕路,只能向右或向下移动。
(2)状态定义
设 a[i][j] 表示第 i 行、第 j 列格子的通行费用。
设 dp[i][j] 表示从左上角 (1,1) 出发,到达格子 (i,j) 所需要支付的最小通行费,包括起点和终点格子的费用。
(3)最优子结构性质
对于任意格子 (i,j),由于只能向右或向下移动,因此只能从上方格子 (i-1,j) 或左方格子 (i,j-1) 到达。
如果到达 (i,j) 的路径是最优路径,那么到达其前一个格子的路径也必须是最优路径。否则,可以用费用更低的路径替换原来的部分路径,从而得到更小的总费用,与原路径最优的假设矛盾。
因此,本题满足最优子结构性质。
(4)递归方程式(状态转移方程)
当 i>1 且 j>1 时:
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + a[i][j]
其中:
dp[i-1][j]表示从上方格子到达当前位置之前的最小费用。dp[i][j-1]表示从左方格子到达当前位置之前的最小费用。min()用于选择费用较小的路径。a[i][j]表示当前位置需要支付的通行费。
(5)边界条件
① 起点:
dp[1][1] = a[1][1]
② 第一行只能从左边到达:
dp[1][j] = dp[1][j-1] + a[1][j],其中 2≤j≤N。
③ 第一列只能从上方到达:
dp[i][1] = dp[i-1][1] + a[i][1],其中 2≤i≤N。
通过以上递归关系,可以逐步计算出到达每个格子的最小费用,最终得到原问题的最优解。
1.2 给出填表法中表的维度、填表范围和填表顺序,以及原问题的最优值是哪个表格元素
(1)表的维度
采用一个二维动态规划数组 dp[N+1][N+1] 存储各个子问题的最优解。
其中使用的有效区域为 N×N,行和列的下标均从 1 开始。
(2)填表范围
填表范围为:
1≤i≤N,1≤j≤N
即需要计算从 dp[1][1] 到 dp[N][N] 的所有有效元素。
(3)填表顺序
采用自底向上的动态规划方法,按照从上到下、从左到右的顺序依次填表。
具体步骤如下:
- 初始化起点
dp[1][1]=a[1][1]。 - 依次计算第一行和第一列的最小费用。
- 从第二行第二列开始,利用状态转移方程计算其余元素。
- 逐行填表,直到计算出右下角元素
dp[N][N]。
由于每个状态只依赖其上方和左方的状态,因此按照上述顺序填表,可以保证计算当前状态时,所依赖的状态已经计算完成。
(4)填表示例
以题目给出的 N=5 的样例为例,得到动态规划表:
| i\j | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 1 | 1 | 5 | 11 | 19 | 29 |
| 2 | 3 | 8 | 15 | 30 | 46 |
| 3 | 9 | 16 | 24 | 42 | 62 |
| 4 | 19 | 27 | 36 | 55 | 76 |
| 5 | 39 | 50 | 61 | 84 | 109 |
例如,计算 dp[3][3]:
dp[3][3] = min(dp[2][3], dp[3][2]) + a[3][3]
= min(15,16) + 9 = 24
(5)原问题的最优值
原问题要求从左上角 (1,1) 到右下角 (N,N) 的最小通行费,因此最优值存储在:
dp[N][N]
对于题目样例:
dp[5][5] = 109
所以样例的最低通行费为 109。
1.3 分析该算法的时间和空间复杂度
(1)时间复杂度
该算法采用双重循环遍历整个 N×N 网格。
对于每一个格子,只需要比较上方和左方两个状态的值,再加上当前格子的通行费,计算过程的时间复杂度为 O(1)。
由于一共需要计算 N² 个状态,因此总时间复杂度为:
O(N²)
(2)空间复杂度
算法需要使用一个二维数组 dp 来保存每个格子的最小通行费。
由于动态规划数组的大小为 N×N,因此空间复杂度为:
O(N²)
如果进一步进行空间优化,由于每个状态只依赖当前行左侧和上一行相同列的状态,可以使用一维数组保存中间结果,将额外空间复杂度降低到 O(N)。
综上,采用二维动态规划填表法时:
- 时间复杂度:O(N²)
- 空间复杂度:O(N²)
2. 你对动态规划算法的理解和体会
通过本次动态规划算法的学习和实践,我对动态规划的基本思想有了更加深入的理解。
我认为,动态规划的核心思想是将一个复杂的问题划分为若干相互关联的子问题,通过保存子问题的计算结果,避免重复计算,从而提高算法的效率。
在解决“最低通行费”问题的过程中,我最开始想到的是枚举所有可能的路径,然后比较各条路径的费用。但是这种方法需要考虑大量不同的路径,随着网格规模增大,计算量也会迅速增加。后来通过分析发现,到达每个格子的最小费用只与上方和左方两个格子有关,因此可以使用动态规划来解决。
通过这道题,我认识到动态规划算法最重要的步骤是正确地定义状态和找出状态转移方程。只有明确了每个状态表示什么,以及当前状态与之前状态之间的关系,才能正确地进行求解。同时,边界条件和填表顺序也十分重要,如果边界初始化错误,或者在依赖的状态尚未计算完成时就进行状态转移,都可能导致最终结果错误。
此外,我也体会到了动态规划与递归算法之间的联系。两者都可以通过子问题求解原问题,但动态规划通过记录已经求解的子问题结果,减少了重复计算,提高了算法效率。
通过这次实践,我不仅掌握了二维动态规划的基本求解方法,也加深了对最优子结构性质、状态转移方程以及时间和空间复杂度分析的理解。
总的来说,我认为学习动态规划不能只记忆状态转移方程,更重要的是理解方程是如何推导出来的。今后遇到类似问题时,我会先分析问题是否具有最优子结构和重叠子问题的特点,再尝试定义状态、确定边界条件和推导状态转移方程,逐步提高自己独立分析和解决算法问题的能力。

浙公网安备 33010602011771号