Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.算法
For example, given the following triangle數組
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
The minimum path sum from top to bottom is 11
(i.e., 2 + 3 + 5 + 1 = 11).優化
Note:this
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.spa
發現了一個很好的動態規劃解題套路。code
先從很是naive的角度看這個題:從三角形的頂開始向下走,你此時並不能肯定在第二層選擇哪個數字,由於看起來大的那個數字也可能會指向一條數字和小的路徑。因此每個元素及每個元素下層的鄰居元素都要遍歷。這不就是典型的backtracking問題麼?blog
咱們知道dp是backtracking的一種優化,主要解決了子問題重疊形成的時間浪費,那麼當咱們對一個問題的dp解法尚未想法的時候,先經過研究backtracking的遞歸樹來看看能不能找到子問題重疊的狀況。遞歸
如下面這個三角形爲例ip
[0],
[1,2],
[3,4,5],
[6,7,8,9]
它的backtracking遞歸樹:get
已經能看出子樹重複了,若是再三角形加一層會更加明顯,能發現七、8這兩個子樹也有高度重複。
到這裏就很是清晰了,題目中要求O(n) extra space,那麼用bottom-up的dp,將每一個節點到最底層的最短路徑記錄下來就能夠了。
dp算法:
dp[row][i]: 第row層第i個元素到達底層所需的最短路徑
轉移方程: dp[row][i] = triangle[i] + min(dp[row+1][i], dp[row+1][i+1])
此處用的是一個二維數組,按題中要求可壓縮成一個一維數組,從底層向高層循環,每次都利用低層的數字算出高層的,而後覆蓋數組中的值便可。
1 public int minimumTotal(List<List<Integer>> triangle) { 2 int N = triangle.size(); 3 int[] min = new int[N]; 4 List<Integer> lastRow = triangle.get(N - 1); 5 for(int i = 0; i < N; i++) 6 min[i] = lastRow.get(i); 7 8 for(int row = N - 2; row >= 0; row--){ 9 List<Integer> currentRow = triangle.get(row); 10 for(int i = 0; i < row + 1; i++){ 11 min[i] = currentRow.get(i) + Math.min(min[i], min[i+1]); 12 } 13 } 14 return min[0]; 15 }
時間複雜度 = O(三角形中數字數)
空間複雜度 = O(三角形層數)
dp是一種「看別人的答案恍然大悟,但下一次仍是不會作」的問題,由於dp的代碼形式並非重點,將會作dp和不會作dp問題的人劃分開來的是他們的思路,即「怎麼想到要用dp,如何得出狀態轉換方程」。
做爲dp新手,一個思路是試着用backtracking解題並畫出遞歸樹,尋找遞歸樹中重疊的子樹,這樣就抓住了dp問題的關鍵,狀態轉換方程會不請自來。