算法第三章作业
- 实践报告:最低通行费的动态规划求解
1.1 最优子结构与递归方程式
问题分析
给定一个 N×N 的网格,每个格子有费用 cost[i][j]。商人从左上角 (0,0) 出发,到达右下角 (N-1,N-1),且必须恰好经过 2N-1 个格子(即只能向右或向下移动)。目标是使总费用最小。
最优子结构
设 dp[i][j] 表示从起点 (0,0) 走到格子 (i,j) 的最小总费用。
要到达 (i,j),只能从上方 (i-1,j) 或左方 (i,j-1) 过来,因此问题的最优解包含其子问题 dp[i-1][j] 和 dp[i][j-1] 的最优解,满足最优子结构性质。
递归方程式
dp[i][j] = cost[i][j] + min( dp[i-1][j] , dp[i][j-1] ) , i>0 且 j>0
• 定义:dp[i][j] 表示从左上角 (0,0) 到达格子 (i,j) 所需的最小费用。
• 边界条件:
• dp[0][0] = cost[0][0] (起点)
• 对于第一行 (i=0, j>0):dp[0][j] = dp[0][j-1] + cost[0][j](只能从左来)
• 对于第一列 (j=0, i>0):dp[i][0] = dp[i-1][0] + cost[i][0](只能从上面来)
1.2 填表法设计
• 表的维度:二维数组 dp[N][N],大小与网格相同。
• 填表范围:所有格子 (i,j),其中 0 ≤ i < N,0 ≤ j < N。
• 填表顺序:按行优先(从上到下,每行从左到右)。因为计算 dp[i][j] 只依赖其上方 (i-1,j) 和左方 (i,j-1),该顺序保证了子问题已被求解。
• 原问题的最优值:表格右下角元素 dp[N-1][N-1]。
1.3 时间复杂度与空间复杂度分析
• 时间复杂度:双层循环遍历所有 N² 个格子,每个格子执行常数次操作,故时间复杂度为 O(N²)。
• 空间复杂度:使用一个 N×N 的二维数组存储 dp 值,故空间复杂度为 O(N²)。
优化:可仅用一维滚动数组将空间降至 O(N),但本题 N≤100,二维即可。
- 对动态规划算法的理解和体会
动态规划的核心思想是 “记住过去,避免重复计算”。它将复杂问题分解为相互重叠的子问题,通过记录子问题的解来构建原问题的解。学习过程中我有以下几点体会:
-
最优子结构是前提
一个问题能用 DP 解决,首先必须具有最优子结构——即原问题的最优解可以由子问题的最优解推导得到。例如本问题中,到达 (i,j) 的最优路径必然包含到达其上方或左方格子的最优路径。识别这一性质是建模的第一步。 -
状态定义与转移方程是关键
DP 的难点在于如何定义状态(dp 的含义)以及写出正确的转移方程。状态要能覆盖所有决策,且无后效性(未来决策不影响已记录的状态)。本题的状态 dp[i][j] 直接对应位置,转移方程由移动规则自然得出。 -
边界条件不可忽视
边界是递推的基石,例如第一行和第一列无法同时从两个方向来,需要单独处理。忽略边界会导致数组越界或逻辑错误。 -
填表顺序决定正确性
必须保证计算某个状态时,它所依赖的所有子状态都已计算完毕。本题按行从左到右、从上到下即可。 -
时空权衡
有时可以通过压缩状态降低空间复杂度(如用一维数组代替二维),但需小心覆盖顺序。初学者应先掌握基础写法,再尝试优化。 -
与实际问题的联系
许多看似复杂的路径规划、资源分配、序列比对等问题,都可以抽象为 DP 模型。刷 LeetCode 时,我会刻意练习“五步法”:确定状态 → 转移方程 → 初始条件 → 计算顺序 → 返回结果。坚持这一框架,能显著提升解题效率。
浙公网安备 33010602011771号