120. Triangle

題目

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問題的關鍵,狀態轉換方程會不請自來。

相關文章
相關標籤/搜索