LeetCode::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: 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.
1、上午开始做这道题。开始没有明白题目的意思。理解成是在每行中求最小值。然后将最小值求和就可以得到结果。一次Wrong Answer。之后才细看题目。题图要表达的从顶部到底部的最短路径和,其中路径一词有玄机。路径表示上边节点到下一层的两个分叉。如上例,存在的路径只有:2364,2361,2351,2358,2451,2458,2478,2473
2、明白题意后,即想到这是一个动态规划问题。思考下转移方程。
f(n,j) = min(f(n-1,j-1),f(n-1,j))+p[i,j]; 貌似有些复杂,我们还得解释一下。
// 转移矩阵 f(n,j) = min(f(n-1 ,j-1), f(n-1 ,j)) + p[n, j]; // f(n, j) 表示从1到n行的,以j为终点的最短路径。 // f(n-1, j) 表示从1到n-1行的,以j为终点的最短路径。 // p[n, j] 表示第n行的第j个元素 对于题目中的例子,我们给出他的状态转移方程构造出来的矩阵 [ [2], [3,4], [6,5,7], [4,1,8,3] ] 转移矩阵如下 [ [2], a. 填入2 [5, 6], b. 5 = 2+3,6 = 2+4 [11,10,13], c. 11 = 5+6, 10 = 5+min(5,6), 13 = 6+7 [15,11,18,16] d. 15 = 11+4, 11 = 1+min(11,10), 18 = 8+min(10,13), 16 = 3+13 ]
3、由上事例可以看出下层的节点的头和尾,因为只有一个直接前驱,所以不需要求min(最小值)这个过程,中间的其余元素都需要比较两个直接前驱中的较小元素。将自身的值和较小元素求和,得到到达自身的最短路径。
4、题目要求的最短路径,针对上面的问题,等价的解答是,找出状态转移矩阵最底层f(n,j)的最小值。便求得由顶到底的最短路径长度。
4、可以分析的都时间复杂度。对于输入N行的三角矩阵,元素个数为N*(N+1)/2, 借助状态转移矩阵,只需要对每个元素扫描一次,所以时间的复杂度为O(N*N)。这个是无法优化的。然而在空间复杂度上却存在优化的方法。如上事例所用的转移矩阵,更具输入规模N来分配,需要O(N*N),联系背包问题中用一维数组滚动覆盖的方法可以实现空间复杂度为O(N)的优化。不过一定注意此时对转移矩阵的更新需要从后往前操作。因为f(n,j)的求解依赖于f(n-1,j)和f(n-1,j-1)。如果从前往后操作会覆盖信息的。这样做就失去使用滚动数组的意义了。
5、代码如下。
// 定义一个状态转移矩阵,Time complexity: O(N*N), Space complexity: O(N) // 最左和最右的元素要另外讨论 int Solution::minimumTotal2(vector<vector<int>> &triangle) { int len = triangle.size(); if(len == 0) return 0; vector<int> dp(len,0); // 状态转移矩阵,记录最短路径 vector<int>::iterator dp_index; vector<vector<int>>::iterator i; vector<int>::iterator j; // 第一行需要另外处理 i = triangle.begin(); dp[0] = *(i->begin()); for(i = triangle.begin()+1; i != triangle.end(); ++i) { dp_index = dp.begin() + i->size() - 1; // 最后一个元素 j = i->end()-1; *dp_index = *(dp_index-1) + *j; --dp_index; // 从后往前才不会覆盖 for(j = i->end()-2; j != i->begin(); --j) { *dp_index = min(*dp_index,*(dp_index-1)) + *j; --dp_index; } // 第一个元素 *dp_index += *(i->begin()); } // 在dp中寻找最小值 int min_sum = *(dp.begin()); for(dp_index = dp.begin()+1; dp_index != dp.end(); ++dp_index) { if(*dp_index < min_sum) min_sum = *dp_index; } return min_sum; }