跳到正文
第 62 题中等

不同路径

Unique Paths

在一个 m × n 的网格中,从左上角走到右下角,每次只能向下或向右,求不同路径数。

在力扣中练习

题目描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

 

示例 1:

输入:m = 3, n = 7
输出:28

示例 2:

输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下

示例 3:

输入:m = 7, n = 3
输出:28

示例 4:

输入:m = 3, n = 3
输出:6

 

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 109

解题思路

主解法

二维 DP · 上方 + 左方

到达每格的路径数,等于上方和左方路径数之和

暴力枚举每条路径会指数爆炸。换个角度:到达格子 (i,j) 的最后一步只有两种可能——从上面下来,或者从左边过来。

所以到达 (i,j) 的路径数,就是这两个格子的路径数相加:

dp[i][j] = dp[i-1][j] + dp[i][j-1]

边界:第一行只能一直向右走,第一列只能一直向下走,路径数都为 1。

自拟主例 m = 3, n = 4。第一行内部格子只有左方来源;进入第二行后,每格开始同时接收上方和左方。右下角的 10 来自上方 4 和左方 6。

分步图解

(1,1):从上方来有 1 条,从左方来有 1 条,共 2 条。

i
1
j
1
不同路径 · 到达每一格的走法数写入答案

dp[i][j]:从左上角走到 (i,j) 的路径数;每一步只能向下或向右

i \ j01230121初dp[0][0]:11dp[0][1]:1,候选来源1dp[0][2]:11dp[0][3]:11dp[1][0]:1,候选来源2dp[1][1]:2,当前目标·dp[1][2]:尚未计算·dp[1][3]:尚未计算1dp[2][0]:1·dp[2][1]:尚未计算·dp[2][2]:尚未计算·dp[2][3]:尚未计算dp[0][1] → dp[1][1]:从上方dp[1][0] → dp[1][1]:从左方
dp[1][1]=dp[0][1] + dp[1][0]
=1 + 1=2
未计算初值
1 / 3
第一行的内部格子只有左方来源;到第二行才开始出现两条来源相加。
分步图解

(2,2):从上方来有 3 条,从左方来有 3 条,共 6 条。

i
2
j
2
不同路径 · 到达每一格的走法数写入答案

dp[i][j]:从左上角走到 (i,j) 的路径数;每一步只能向下或向右

i \ j01230121初dp[0][0]:11dp[0][1]:11dp[0][2]:11dp[0][3]:11dp[1][0]:12dp[1][1]:23dp[1][2]:3,候选来源4dp[1][3]:41dp[2][0]:13dp[2][1]:3,候选来源6dp[2][2]:6,当前目标·dp[2][3]:尚未计算dp[1][2] → dp[2][2]:从上方dp[2][1] → dp[2][2]:从左方
dp[2][2]=dp[1][2] + dp[2][1]
=3 + 3=6
未计算初值
1 / 3
右下角的 10 来自上方 4 和左方 6;和逐步累加,不是取最大值。

按行从左到右填表,计算 (i,j) 时上方和左方都已经确定。时间 O(m × n),空间 O(m × n)。

时间 O(m × n)空间 O(m × n)

易错点

  • 边界只初始化第一行,漏掉第一列:第二行第一格会从 0 开始,后面全错。
  • 把加法写成 max:路径数是累加,不是取最优。
  • m 或 n 为 1:循环不进入,必须靠边界初始化直接返回 1。
对照解法

滚动数组 · 一维 DP

一行一行往下滚,dp[j] 覆盖前就是上方值

时间相同,空间从 O(m × n) 降到 O(n);代价是只保留上一行,不能直接还原具体路径。

转移只依赖上一行和本行左边,所以可以只保留一行。

dp[j] 在更新前是上方的路径数,更新后是本行的路径数;左边的 dp[j-1] 已经在本次行内更新过,正好是左方的值。于是

dp[j] = dp[j] + dp[j-1]

第一行全部初始化为 1;之后每行从 j=1 开始向右更新。

时间 O(m × n),空间 O(n)。如果题目要求输出具体路径,就不能只留一行,需要额外记录选择。

  1. 1dp 长度为 n,全部初始化为 1(第一行)。
  2. 2i 从 1 枚举到 m-1。
  3. 3j 从 1 枚举到 n-1:dp[j] += dp[j-1]。
  4. 4返回 dp[n-1]。
时间 O(m × n)空间 O(n)

易错点

  • j 从 0 开始:dp[0] 会被加两次,第一列不再是 1。
  • 更新顺序从右往左:dp[j-1] 还是上一行的值,左方来源就丢了。

代码与演示

主解法配有图解;对照解法用于比较思路、复杂度或实现方式。

演示输入m = 3, n = 4

第一行只能一直向右走,第一列只能一直向下走,路径数都是 1。

m × n
3 × 4
不同路径 · 到达每一格的走法数建立初值

dp[i][j]:从左上角走到 (i,j) 的路径数;每一步只能向下或向右

i \ j01230121初dp[0][0]:1,当前目标1dp[0][1]:11dp[0][2]:11dp[0][3]:11dp[1][0]:1·dp[1][1]:尚未计算·dp[1][2]:尚未计算·dp[1][3]:尚未计算1dp[2][0]:1·dp[2][1]:尚未计算·dp[2][2]:尚未计算·dp[2][3]:尚未计算
dp[0][0]=1
未计算初值
1 / 8
查看 C++ 代码

延伸阅读

  • 不同路径 II(63)在格子里加了障碍:遇到障碍时 dp 直接置 0,转移形式不变。
  • 这也是组合数问题:从 (0,0) 到 (m-1,n-1) 要走 m+n-2 步,其中选 m-1 步向下,答案是 C(m+n-2, m-1)。