Medium!
題目描述:
給定一個三角形,找出自頂向下的最小路徑和。每一步只能移動到下一行中相鄰的結點上。
例如,給定三角形:
[ [2], [3,4], [6,5,7], [4,1,8,3] ]
自頂向下的最小路徑和為 11
(即,2 + 3 + 5 + 1 = 11)。
說明:
如果你可以只使用 O(n) 的額外空間(n 為三角形的總行數)來解決這個問題,那么你的算法會很加分。
解題思路:
這道題和Dungeon Game 地牢游戲非常的類似,都是用動態規划Dynamic Programming來求解的問題。而且遞推式也比較容易看出來,最先想到的方法是:
從第二行開始,triangle[i][j] = min(triangle[i - 1][j - 1], triangle[i - 1][j]), 然后兩邊的數字直接賦值上一行的邊界值,由於限制了空間復雜度,所以干脆直接就更新triangle數組。
C++解法一:
1 class Solution { 2 public: 3 int minimumTotal(vector<vector<int> > &triangle) { 4 int n = triangle.size(); 5 for (int i = 1; i < n; ++i) { 6 for (int j = 0; j < triangle[i].size(); ++j) { 7 if (j == 0) triangle[i][j] += triangle[i - 1][j]; 8 else if (j == triangle[i].size() - 1) triangle[i][j] += triangle[i - 1][j - 1]; 9 else { 10 triangle[i][j] += min(triangle[i - 1][j - 1], triangle[i - 1][j]); 11 } 12 } 13 } 14 int res = triangle[n - 1][0]; 15 for (int i = 0; i < triangle[n - 1].size(); ++i) { 16 res = min(res, triangle[n - 1][i]); 17 } 18 return res; 19 } 20 };
這種方法可以通過OJ,但是畢竟修改了原始數組triangle,並不是很理想的方法。在網上搜到一種更好的DP方法,這種方法復制了三角形最后一行,作為用來更新的一維數組。然后逐個遍歷這個DP數組,對於每個數字,和它之后的元素比較選擇較小的再加上上面一行相鄰位置的元素做為新的元素,然后一層一層的向上掃描,整個過程和冒泡排序的原理差不多,最后最小的元素都冒到前面,第一個元素即為所求。
C++解法二:
1 class Solution { 2 public: 3 int minimumTotal(vector<vector<int> > &triangle) { 4 int n = triangle.size(); 5 vector<int> dp(triangle.back()); 6 for (int i = n - 2; i >= 0; --i) { 7 for (int j = 0; j <= i; ++j) { 8 dp[j] = min(dp[j], dp[j + 1]) + triangle[i][j]; 9 } 10 } 11 return dp[0]; 12 } 13 };
下面我們來看一個例子,對於輸入數組:
-1
2 3
1 -1 -3
5 3 -1 2
下面我們來看DP數組的變換過程。
DP:5 3 -1 2
DP:4 3 -1 2
DP:4 -2 -1 2
DP:4 -2 -4 2
DP:0 -2 -4 2
DP:0 -1 -4 2
DP:-2 -1 -4 2