这是Leetcode对动态规划的第10题,也是历史相当久远的一道题。之前动态规划的思想倒是了解差不多,但还是没能顺利写出来,就其原因是边界条件没有考虑周到,还是按照常规的矩阵逻辑求解三角路径。
三角形最小路径 题目描述
给定一个三角形 triangle
,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i
,那么下一步可以移动到下一行的下标 i
或 i + 1
。
三角形最小路径 求解方法
其实这个状态方程还是很容易想到的,按照题目提示,对于(i,j)点,其只能由(i,j-1)或(i-1,j-1)抵达,且对于题目描述中给出的三角形定义,是一个等腰直角三角形,路径和三角如图所示。
画着画着就画多了,也是一个思考历程吧。图中f代表我们定义的状态矩阵,c代表给出的三角形。这里depth和width实际上是相等关系。
习惯上,我们会考虑某一点的下一步怎么走,比如在三角形中,初始状态的下一步只能朝下,或者右下走。但动态规划的解题思路往往是倒着来的(我的理解)。为了求解出解决问题的最小结构,应该去思考“要达到某一点”,“从哪里能过来”。
对于最常规的(i,j),也就是紫色框, 很显然,我们要求的f[i][j]是当前位置三角形中的值,加上来自上一行(第i-1行)中第j列和第j-1列的最小值,这个情况下的转移方程就写好了。
而对于j=0的时候,我们发现这个方程会出现指向空的现象,这时候就需要考虑这种边界情况。也就是说对于没有左侧列的(i,j),f的值只能为当前位置三角形的存储值加上一行的f值,也就是橙色框展示的。
接下来考虑对角线元素,我们发现,他们只能通过其左上方元素抵达,有了先前的经验,转移关系也比较好写了,就像红色框一样。
最后,自顶向下运动的路径和存储在f的最后一行,只需要返回这个矩阵最后一行的最小元素就可以。
解题代码如下:
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int depth = triangle.size();
vector<vector<int>> rectangle(depth,vector<int>(depth));
rectangle[0][0] = triangle[0][0];
for(int i = 1; i < depth; i++){
rectangle[i][0] = triangle[i][0] + rectangle[i-1][0];
for(int j = 1; j < i; j++){
rectangle[i][j] = min(rectangle[i-1][j],rectangle[i-1][j-1])+triangle[i][j];
}
rectangle[i][i] = triangle[i][i] + rectangle[i-1][i-1];
}
return *min_element(rectangle[depth-1].begin(),rectangle[depth-1].end());
}
};
需要注意的是,代码中的rectangle也就是图中的f,【因为一开始的解题思路是定义里一个depth,width的矩阵哈哈哈哈,结果depth和width一样,width代码中删掉了】
其实官解讲的也很清除,很推荐哈~
下降路径最小和 题目描述
给你一个 n x n
的 方形 整数数组 matrix
,请你找出并返回通过 matrix
的下降路径 的 最小和 。
下降路径 可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列(即位于正下方或者沿对角线向左或者向右的第一个元素)。具体来说,位置 (row, col)
的下一个元素应当是 (row + 1, col - 1)
、(row + 1, col)
或者 (row + 1, col + 1)
。
下降路径最小和 题目描述
在理解三角路径后,聪明的你对矩形路径一定能够融会贯通。个人认为理解了三角路径后,矩形路径也是十分形象的,区别在于矩形路径要考虑两侧的列。emm如果不好理解的话就看个图吧~
class Solution {
public:
int minFallingPathSum(vector<vector<int>>& matrix) {
int width = matrix.size();
vector<vector<int>> f(width,vector<int>(width));
for(int i = 0; i < width; i++){
f[0][i] = matrix[0][i];
}
for(int i = 1; i < width; i++){
f[i][0] = min(f[i-1][0],f[i-1][1]) + matrix[i][0];
for(int j = 1; j < width-1; j++){
f[i][j] = min({f[i-1][j-1],f[i-1][j],f[i-1][j+1]}) + matrix[i][j];
}
f[i][width-1] = min(f[i-1][width - 2],f[i-1][width-1]) + matrix[i][width-1];
}
return *min_element(f[width-1].begin(),f[width-1].end());
}
};
解题代码附上,应该是很好理解,在这段代码中matrix也就是图中的c,f就是图中的f。需要注意的是,在找三个值的最小值是,需要用到min({a,b,c}),算是一个小知识点,别忘了大括号,码住~