1. 问题背景与理解
Unique Paths是LeetCode上一道经典的动态规划问题,编号为62。题目描述如下:一个机器人位于m×n网格的左上角,每次只能向下或向右移动一步,问到达网格右下角有多少条不同的路径。
这道题之所以经典,是因为它完美展现了动态规划的核心思想——将大问题分解为子问题,并存储子问题的解以避免重复计算。我在第一次遇到这个问题时,尝试用递归暴力破解,结果在稍大的网格(比如19×13)就触发了超时。这让我意识到必须寻找更优解法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动态规划解法详解
2.1 状态定义与转移方程
定义dp[i][j]表示到达(i,j)位置的路径数。根据移动规则,机器人只能从上方或左方来到当前位置,因此状态转移方程为:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
边界条件是第一行和第一列的所有位置都只有1种路径(只能一直向右或向下)。
python复制def uniquePaths(m: int, n: int) -> int:
dp = [[1]*n for _ in range(m)]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
2.2 空间优化技巧
观察状态转移方程可以发现,我们只需要前一行的数据即可计算当前行。因此可以将空间复杂度从O(mn)优化到O(n):
python复制def uniquePaths(m: int, n: int) -> int:
dp = [1] * n
for _ in range(1, m):
for j in range(1, n):
dp[j] += dp[j-1]
return dp[-1]
注意:在面试中如果能主动提出空间优化方案,通常会获得加分。建议先写出标准二维DP解法,再优化为一维。
3. 组合数学解法
3.1 数学原理分析
这个问题本质上是一个组合问题:从起点到终点需要移动(m-1)+(n-1)步,其中(m-1)步向下,(n-1)步向右。不同的路径就是这些移动步骤的不同排列组合数。
因此路径总数为组合数C(m+n-2, m-1)或C(m+n-2, n-1)。
3.2 实现与注意事项
python复制import math
def uniquePaths(m: int, n: int) -> int:
return math.comb(m+n-2, m-1)
警告:在实际计算组合数时,要注意避免直接计算阶乘导致的整数溢出。Python的math.comb()内部已经做了优化,但如果是用其他语言实现,建议使用递推公式计算组合数。
4. 常见错误与调试技巧
4.1 边界条件处理
新手常犯的错误包括:
- 忘记初始化第一行和第一列为1
- 循环变量从0开始导致数组越界
- 混淆m和n的行列对应关系
建议在纸上画出3×2的小网格手动推导,验证代码的正确性。
4.2 效率对比
测试数据:m=23, n=12
- 递归解法:超时
- 二维DP:0.03ms
- 一维DP:0.02ms
- 数学解法:0.01ms
5. 变种问题拓展
5.1 带障碍物的版本
LeetCode 63题在网格中加入了障碍物,解决思路是:
- 遇到障碍物时dp[i][j]=0
- 初始化第一行/列时,遇到障碍物后所有位置都不可达
5.2 最小路径和
LeetCode 64题要求找路径最小和,状态转移方程变为:
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
6. 面试应用技巧
在面试中遇到此类问题时,建议按以下步骤展开:
- 先阐述暴力递归的思路和时间复杂度
- 指出重复计算问题,引入动态规划
- 写出状态转移方程和边界条件
- 讨论空间优化可能性
- 如果时间允许,可以提及数学解法
我在多次面试中用这个问题考察候选人,发现能完整走完这个思考流程的候选人,通常对动态规划有扎实的理解。一个实用的技巧是:先处理特殊情况(如m或n为1),可以简化主逻辑的编写。
