題目描述
一個機器人位於一個 m x n 網格的左上角 (起始點在下圖中標記爲“Start” )。
機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角(在下圖中標記爲“Finish”)。
現在考慮網格中有障礙物。那麼從左上角到右下角將會有多少條不同的路徑?
網格中的障礙物和空位置分別用 1 和 0 來表示。
說明:m 和 n 的值均不超過 100。
示例 1:
輸入: [ [0,0,0], [0,1,0], [0,0,0] ]
輸出: 2
解釋:
3x3 網格的正中間有一個障礙物。
從左上角到右下角一共有 2 條不同的路徑:
- 向右 -> 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右 -> 向右
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/unique-paths-ii
解題思路
我們用 來表示從座標 到座標 的路徑總數, 表示座標 是否可行,如果座標 有障礙物,,否則 。
因爲機器人每次只能向下或者向右移動一步
,所以 到座標 的路徑總數的值只取決於從座標 到座標 的路徑總數和從座標 到座標 的路徑總數,即 只能通過 和 轉移得到。
當座標 本身有障礙的時候:任何路徑都到到不了 ,此時 。
當座標 沒有障礙的時候:如果座標 沒有障礙,那麼就意味着從座標 可以走到 ,即 位置對 的貢獻爲 ;同理,當座標 沒有障礙的時候, 位置對 的貢獻爲 。
綜上所述,我們可以得到這樣的動態規劃轉移方程:
由於這裏 只與 和 相關,我們可以運用滾動數組思想
把空間複雜度優化稱 。
滾動數組思想是一種常見的動態規劃優化方法,在題目中已經多次使用到,例如「劍指 Offer 46. 把數字翻譯成字符串」、「70. 爬樓梯」等,當定義的狀態在動態規劃的轉移方程中只和某幾個狀態相關的時候,就可以考慮這種優化方法,目的是給空間複雜度「降維」。
- 複雜度分析
- 時間複雜度:,其中 爲網格的行數, 爲網格的列數。我們只需要遍歷所有網格一次即可。
- 空間複雜度:。利用滾動數組優化,我們可以只用 大小的空間來記錄當前行的 值。
代碼實現
class Solution(object):
def uniquePathsWithObstacles(self, obstacleGrid):
"""
:type obstacleGrid: List[List[int]]
:rtype: int
"""
m = len(obstacleGrid)
n = len(obstacleGrid[0])
dp = [1] + [0]*(n-1)
for i in range(m):
for j in range(n):
if j == 0:
dp[j] = 0 if obstacleGrid[i][j] else dp[j]
else:
dp[j] = 0 if obstacleGrid[i][j] else dp[j] + dp[j-1]
return dp[-1]
Tips
怎麼想到用動態規劃來解決這個問題呢?我們需要從問題本身出發,尋找一些有用的信息,例如本題中:
- 位置只能從 和 走到,這樣的條件就是在告訴我們這裏轉移是
無後效性
的, 和任何的 無關。 - 動態規劃的題目分爲兩大類,一種是
求最優解類
,典型問題是揹包問題,另一種就是計數類
,比如這裏的統計方案數的問題,它們都存在一定的遞推性質
。前者的遞推性質還有一個名字,叫做最優子結構
——即當前問題的最優解取決於子問題的最優解,後者類似,當前問題的方案數取決於子問題的方案數。所以在遇到求方案數的問題時,我們可以往動態規劃的方向考慮。
通常如果我們察覺到了這兩點要素,這個問題八成可以用動態規劃來解決。
根據官方題解總結。