題目
一個機器人位於一個 m x n 網格的左上角 (起始點在下圖中標記爲“Start” )。
機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角(在下圖中標記爲“Finish”)。
問總共有多少條不同的路徑?
例如,上圖是一個7 x 3 的網格。有多少可能的路徑?
示例 1:
輸入: m = 3, n = 2
輸出: 3
解釋:
從左上角開始,總共有 3 條路徑可以到達右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
示例 2:
輸入: m = 7, n = 3
輸出: 28
提示:
- 1 <= m, n <= 100
- 題目數據保證答案小於等於 2 * 10 ^ 9
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/unique-paths
思路
這個題目和爬樓梯問題很像。
很容易想出遞歸解法。機器人每次只能向下或者向右移動一步,網格右下角記爲終點,到達終點有兩種方法: 從終點位置上面往下走一步到達;從左邊往右走一步到達。
所以只要用反向的思維就可以寫出遞歸代碼了。注意這是一個n行m列的網格。
代碼
遞歸
class Solution(object):
def unique(self, m, n):
# 如果到達了起點,就是一種解法
if m == 0 and n == 0:
return 1
ways = 0
# 如果可以向左回退一步
if m >= 1:
ways = self.unique(m - 1, n)
# 如果可以向上回退一步
if n >= 1:
ways += self.unique(m, n - 1)
return ways
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
# 終點是m-1,n-1
return self.unique(m-1,n-1)
雖然思路是對的,但是遞歸的方式是比較低效的,這裏導致了計算超時。參閱LeetCode刷題之動態規劃思想,我們可以將其改成記憶化搜索的方式,解決重疊子問題。
記憶化搜索
dp = {(0,0) : 1} #起點
class Solution(object):
def unique(self, m, n):
if (m,n) not in dp:
ways = 0
# 如果可以向左回退一步
if m >= 1:
ways = self.unique(m - 1, n)
# 如果可以向上回退一步
if n >= 1:
ways += self.unique(m, n - 1)
dp[(m,n)] = ways
return dp[(m,n)]
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
# 終點是m-1,n-1
return self.unique(m-1,n-1)
動態規劃
動態規劃的解法也很簡單,從起點開始考慮。dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
這句代碼表示要達到第[i][j]
個格子,可以有它的左邊那麼格子或上面那個格子達到。
我們只要處理好邊界值,整個代碼就很清爽。這點先處理邊界值的思想也體現在了64. 最小路徑和問題裏面。
class Solution(object):
def uniquePaths(self, m, n):
# n行 m 列的列表 外層不能使用*,否則會淺拷貝內層
dp = [[0] * m for _ in range(n)]
# 先設定好邊界值
for i in range(0, n):
dp[i][0] = 1
for j in range(1, m):
dp[0][j] = 1
for i in range(1, n):
for j in range(1, m):
# dp[i][j] = 左邊一步加上面一步的結果
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
return dp[-1][-1]
刷到了8ms。