《每日一題》62. Unique Paths 不同路徑
技術標籤:# LeetCode
一個機器人位於一個 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
DFS
今天這題那麼簡單???直接深搜就能出結果???不可能吧。
Python
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
def dfs(x: int, y: int):
# print(f"x = {x}, y = {y}")
if x == m - 1 and y == n - 1:
return 1
if x > m - 1 or y > n - 1:
return 0
return dfs(x + 1, y) + dfs(x, y + 1)
return dfs(0, 0)
果然超時了,沒過,DFS不行那就得DP了。
DP
我們用 f(i,j)f(i, j)f(i,j) 表示從左上角走到 (i,j)(i, j)(i,j) 的路徑數量,其中 iii 和 jjj 的範圍分別是 [0,m)[0, m)
由於我們每一步只能從向下或者向右移動一步,因此要想走到 (i,j)(i, j)(i,j),如果向下走一步,那麼會從 (i−1,j)(i-1, j)(i−1,j) 走過來;如果向右走一步,那麼會從 (i,j−1)(i, j-1)(i,j−1) 走過來。因此我們可以寫出動態規劃轉移方程:
f(i,j)=f(i−1,j)+f(i,j−1)f(i, j) = f(i-1, j) + f(i, j-1) f(i,j)=f(i−1,j)+f(i,j−1)
需要注意的是,如果 i=0i=0i=0,那麼 f(i−1,j)f(i-1,j)f(i−1,j) 並不是一個滿足要求的狀態,我們需要忽略這一項;同理,如果 j=0j=0j=0,那麼 f(i,j−1)f(i,j-1)f(i,j−1) 並不是一個滿足要求的狀態,我們需要忽略這一項。
初始條件為 f(0,0)=1f(0,0)=1f(0,0)=1,即從左上角走到左上角有一種方法。
最終的答案即為 f(m−1,n−1)f(m-1,n-1)f(m−1,n−1)。
Python
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp = [[1] * n] + [[1] + [0] * (n - 1) for _ in range(m - 1)]
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[m - 1][n - 1]
複雜度分析
-
時間複雜度:O(mn)O(mn)O(mn)。
-
空間複雜度:O(mn)O(mn)O(mn),即為儲存所有狀態需要的空間。注意到 f(i,j)f(i, j)f(i,j) 僅與第 iii 行和第 i−1i-1i−1 行的狀態有關,因此我們可以使用滾動陣列代替程式碼中的二維陣列,使空間複雜度降低為 O(n)O(n)O(n)。此外,由於我們交換行列的值並不會對答案產生影響,因此我們總可以通過交換 mmm 和 nnn 使得 m≤nm \leq nm≤n,這樣空間複雜度降低至 O(min(m,n))O(\min(m, n))O(min(m,n))。
滾動陣列實現
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp = [[1] * n] + [[1] + [0] * (n - 1)]
for i in range(1, m):
for j in range(1, n):
dp[i % 2][j] = dp[abs(i % 2 - 1)][j] + dp[i % 2][j - 1]
return dp[abs(m % 2 - 1)][n - 1]
組合數學
從左上角到右下角的過程中,我們需要移動 m+n−2m+n-2m+n−2 次,其中有 m−1m-1m−1 次向下移動,n−1n-1n−1 次向右移動。因此路徑的總數,就等於從 m+n−2m+n-2m+n−2 次移動中選擇 m−1m-1m−1 次向下移動的方案數,即組合數:
C m + n − 2 m − 1 = ( m + n − 2 m − 1 ) = ( m + n − 2 ) ! ( m − 1 ) ! ( n − 1 ) ! C_{m+n-2}^{m-1}=\begin{pmatrix} m+n-2\\m-1\end{pmatrix}=\frac{(m+n-2)!}{(m-1)!(n-1)!} Cm+n−2m−1=(m+n−2m−1)=(m−1)!(n−1)!(m+n−2)!
Python
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
return comb(m + n - 2, n - 1)
複雜度分析
-
時間複雜度:O(m)O(m)O(m)。由於我們交換行列的值並不會對答案產生影響,因此我們總可以通過交換 mmm 和 nnn 使得 m≤nm \leq nm≤n,這樣空間複雜度降低至 O(min(m,n))O(\min(m, n))O(min(m,n))。
-
空間複雜度:O(1)O(1)O(1)。