1. 问题描述与理解
LeetCode 64题"最小路径和"是一个经典的动态规划问题。给定一个包含非负整数的m×n网格,每次只能向下或者向右移动一步,需要找到从左上角到右下角的路径,使得路径上的数字总和为最小。
这个问题在实际中有很多应用场景,比如:
- 物流配送中的最短成本路径规划
- 游戏中的AI寻路算法
- 机器人导航中的最优路径选择
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 解题思路分析
2.1 暴力解法
最直观的想法是使用递归回溯,尝试所有可能的路径并计算最小值。这种方法的时间复杂度是指数级的O(2^(m+n)),显然不适合较大的网格。
python复制def minPathSum(grid):
def helper(i, j):
if i == len(grid)-1 and j == len(grid[0])-1:
return grid[i][j]
if i >= len(grid) or j >= len(grid[0]):
return float('inf')
return grid[i][j] + min(helper(i+1,j), helper(i,j+1))
return helper(0,0)
2.2 动态规划解法
动态规划是解决这类问题的最佳选择。我们可以创建一个与原始网格大小相同的dp数组,其中dp[i][j]表示从(0,0)到(i,j)的最小路径和。
状态转移方程:
dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])
边界条件:
- 第一行只能从左向右移动
- 第一列只能从上向下移动
3. 代码实现与优化
3.1 基础DP实现
python复制def minPathSum(grid):
m, n = len(grid), len(grid[0])
dp = [[0]*n for _ in range(m)]
dp[0][0] = grid[0][0]
# 初始化第一行
for j in range(1, n):
dp[0][j] = dp[0][j-1] + grid[0][j]
# 初始化第一列
for i in range(1, m):
dp[i][0] = dp[i-1][0] + grid[i][0]
# 填充剩余部分
for i in range(1, m):
for j in range(1, n):
dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])
return dp[-1][-1]
时间复杂度:O(mn)
空间复杂度:O(mn)
3.2 空间优化版本
观察状态转移方程,我们发现dp[i][j]只依赖于左边和上边的值,因此可以将空间复杂度优化到O(n):
python复制def minPathSum(grid):
m, n = len(grid), len(grid[0])
dp = [0]*n
dp[0] = grid[0][0]
# 初始化第一行
for j in range(1, n):
dp[j] = dp[j-1] + grid[0][j]
# 填充剩余行
for i in range(1, m):
dp[0] += grid[i][0] # 更新第一列
for j in range(1, n):
dp[j] = grid[i][j] + min(dp[j-1], dp[j])
return dp[-1]
4. 边界条件与特殊情况处理
在实际编码中,我们需要考虑一些特殊情况:
- 空网格或单元素网格
- 只有一行或一列的网格
- 包含0值的网格
- 极大值的情况(防止整数溢出)
5. 测试用例设计
好的测试用例应该包含:
- 常规情况
- 边界情况
- 特殊情况
示例测试用例:
python复制test_cases = [
([[1,3,1],[1,5,1],[4,2,1]], 7), # 常规
([[1,2,3],[4,5,6]], 12), # 只有两行
([[1]], 1), # 单元素
([[1,2],[3,4]], 7), # 2x2网格
([[0,0],[0,0]], 0) # 全0
]
6. 常见错误与调试技巧
新手常犯的错误包括:
- 忘记初始化第一行和第一列
- 边界条件处理不当
- 索引越界
- 空间优化时覆盖了需要的数据
调试技巧:
- 打印中间dp数组的值
- 使用小网格手动计算验证
- 添加详细的日志输出
7. 算法扩展与变种
这个问题有几个有趣的变种:
- 允许对角线移动
- 路径上有障碍物(类似LeetCode 63)
- 需要输出具体路径而不仅仅是和
- 三维网格的最小路径和
8. 实际应用与优化
在实际工程中,我们可以进一步优化:
- 使用原地修改,不额外创建dp数组
- 并行计算(对于超大网格)
- 使用A*等启发式算法加速搜索
9. 性能分析与比较
让我们比较不同解法的性能:
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 暴力递归 | O(2^(m+n)) | O(m+n) | 仅用于教学 |
| 基础DP | O(mn) | O(mn) | 通用解法 |
| 优化DP | O(mn) | O(n) | 推荐使用 |
| Dijkstra | O(mnlog(mn)) | O(mn) | 含负权值时 |
10. 学习建议与进阶路线
要掌握这类动态规划问题,建议:
- 从简单的1维DP开始(如斐波那契数列)
- 练习经典的2维DP问题(如本题、编辑距离等)
- 理解状态转移方程的推导过程
- 尝试自己设计状态和转移方程
进阶学习路线:
- LeetCode 62. 不同路径
- LeetCode 63. 不同路径 II
- LeetCode 120. 三角形最小路径和
- LeetCode 174. 地下城游戏
