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