LeetCode 62,從動態規劃想到更好的解法
- 2020 年 5 月 11 日
- 筆記
- leetcode, LeetCode題解, 演算法
本文始發於個人公眾號:TechFlow,原創不易,求個關注
今天是LeetCode專題第36篇文章,我們一起來看下LeetCode的62題,Unique Paths。
題意
其實這是一道老掉牙的題目了,我在高中資訊競賽的選拔考試上就見過這題。可想而知它有多古老,或者說多經典吧。一般來說能夠流傳幾十年的演算法題,一定是經典中的經典。下面我們就來看下它的題意。
這題的題意很簡單,給定一個矩形的迷宮,左上角有一個機器人,右下角是目的地。這個機器人只能向下走或者是向右走,請問這個機器人走到目的地的路徑一共有多少種?
這題很良心地給定了條件,矩形的長和寬都不超過100.
樣例
Input: m = 3, n = 2
Output: 3
Explanation:
From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Right -> Down
2. Right -> Down -> Right
3. Down -> Right -> Right
Input: m = 7, n = 3
Output: 28
解法
在我寫題解的時候,我突然想起來上一次見到它好像是在某個綜藝節目當中。它被作為一道智力題來考一些明星嘉賓,好像一眾明星裡面,只有關曉彤做了出來。。。
它作為智力題有一個標準的模板式解法:
對於圖中的C點來說,從起點通往它的路徑數量等於通往A點和B點路徑的和。
這個結論我們很多人都知道,因為C點只有兩個來源,一個是A點一個是B點。它既可以從A點來,也可以從B點來,所以應該是一個加和的關係。
這當然是沒錯的,但不知道大家從這個過程當中有沒有什麼感悟。C點的上游是A點和B點,也就是說C狀態是由A狀態或者是B狀態轉移到的。這不就是一個動態規劃演算法嗎?
我們用dp記錄每一個位置的答案的話,那麼可以很輕鬆地寫出狀態轉移方程:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
我們用程式碼把這個方程實現就能解出問題了。
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp = [[0 for _ in range(n+2)] for _ in range(m+2)]
dp[0][1] = 1
for i in range(1, m+1):
# 特殊處理第一列,因為第一列只有1種
dp[i][1] = dp[i-1][1]
for j in range(2, n+1):
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[m][n]
one more solution
如果只有上面這一種解法,那麼這道題完全沒有說的必要,也太簡單了。這道題還有另外一種解法,會更加簡單。
在上面的解法當中,我們是把這個問題當成了演算法題來解決的,使用動態規劃演算法進行建模,適配題目來解決它。但如果我們換個思路,完全可以把它當做數學問題來解。
我們來分析一下問題,機器人要從左上角走到右下角,地圖是沒有缺陷的,所有點都可以到達。由於機器人沒辦法走回頭路,也就是說機器人在通往終點的過程當中走過的路程是確定的。也就是要走n-1條橫邊和m-1條豎邊。
邊的總數和種類都確定了,其實這個問題可以轉化一下。我們把走的橫邊看成是白球,走的豎邊看成是黑球,那麼這道題其實就可以轉化成,我們有n-1個白球,m-1個黑球,現在把它們排成一排,一共有多少種方法?
這個是小學的組合數學問題,我們要從整體的n+m-2個物體當中,選出n-1個,那麼顯然答案就是:
不過,雖然我們用一個式子就表達了,但是要求解這個組合數,還是需要通過循環的。我們把它轉化成:
接著我們用循環求解即可。
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
ret = 1
for i in range(1, n):
ret = ret * (n+m-1-i) // i
return ret
相比於上面的做法更加簡短,雖然兩者看起來都是的演算法,好像差別不大。但是每個數的階乘和組合數都是可以預處理的,在頻繁求解的場景下,顯然要比動態規劃演算法更快。
結尾
這道題就算是講完了,大家會發現LeetCode過了50題之後,涉及到新的演算法明顯變少了。畢竟LeetCode並不是高強度的演算法平台,主打的還是演算法入門。這也限制了它的難度無法和codeforces、topcoder這種主打演算法競賽的平台相比。所以很多人刷到一百題左右的時候,就會覺得沒意思。
感覺很多題目大同小異,新鮮感和正回饋都變少了。這個時候就需要我們把問題做精,多去思考和求解最完美的解法。即使是為了應付面試而刷題,這也是很有必要的。高水平的面試官往往都不會出LeetCode上的原題,都會加一些變化來考察選手,甚至有些大牛會出原創題。光摸清套路是不夠的,我們還需要更加深入的理解。
套用一句電影台詞作為結尾:we need to go deeper.
今天的文章就到這裡,看官大人,請關注我吧