题目描述
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。
例如,给定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为11(即,2 + 3 + 5 + 1 = 11)。
说明:
如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。
题解一
这道题目是一个典型的动态规划的问题。
假设矩阵triangle[m][n]表示三角形的顶点。f[i][j]为从最上层顶点triangle[0][0]到triangle[i][j]的最小路径和,那么状态转移方程为:
if(j == 0) {
f[i][0] = f[i-1][0]+triangle[i][0]
}else if(j == i){
f[i][j] = f[i-1][j-1]+triangle[i][i]
}else {
f[i][j] = min(f[i-1][j], f[i-1][j-1])+triangle[i][j]
}
很显然f[0][0] = triangle[0][0];
代码如下:
/// 三角形最小路径
/// @param triangle 表示三角形的二维矩阵
/// @param triangleSize 矩阵的行数
/// @param triangleColSize 好像没有用到
int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
int ans[triangleSize][triangleSize];
memset(ans, 0, sizeof(ans));
ans[0][0] = triangle[0][0];
for(int i = 1; i < triangleSize; i++) {
ans[i][0] = ans[i-1][0]+triangle[i][0];
for(int j = 1; j < i; j++) {
ans[i][j] = fmin(ans[i-1][j], ans[i-1][j-1])+triangle[i][j];
}
ans[i][i] = ans[i-1][i-1]+triangle[i][i];
}
int result = ans[triangleSize-1][0];
for(int i = 1; i < triangleSize; i++) {
result = fmin(result, ans[triangleSize-1][i]);
}
return result;
}
时间复杂度O(n²),空间复杂度O(n²)
题解二
上一个解法中求出了从顶点到任意一点的最小路径,其实题目要求的只是从顶点到最小层的距离,对上一题的解法可以进行优化,减少空间复杂度为O(n)。
状态转移方程式并没有发生变化,变化的是我们只用一个一维数组来记录每一次循环的结果,在下一次循环过程中更新这个一维数组。需要注意的是,数组的更新需要从每一层逆序更新,否则的话会出现数据错乱的情况。
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
在本例当中,
第一层:f[0] = 2;
第二层:
1. 正序计算:
f[0] = f[0]+triangle[1][0] = 2+3 = 5;
f[1] = f[0]+triangle[1][1] = 5+4 = 9;出错,因为f[0]已经被更新了。
2. 逆序计算:
f[1] = f[0]+triangle[1][1] = 2+4 = 6;
f[0] = f[0]+triangle[1][0] = 2+3 = 5;正确
第三层:
逆序计算:
f[2] = f[1]+triangle[2][2] = 6+7 = 13;
f[1] = min(f[1], f[0])+triangle[2][1] = 5+5 = 10;
f[0] = f[0]+triangle[2][0] = 5+6 = 11;
....
代码如下:
int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
int f[triangleSize];
memset(f, 0, sizeof(f));
f[0] = triangle[0][0];
for(int i = 1; i < triangleSize; i++) {
f[i] = f[i-1] + triangle[i][i];
for(int j = i-1; j > 0; j--) {
f[j] = min(f[j-1], f[j])+triangle[i][j];
}
f[0] = f[0]+triangle[i][0];
}
int res = f[0];
for(int i = 1; i < triangleSize; i++) {
res = min(res, f[i]);
}
return res;
}
时间复杂度O(n²),空间复杂度O(n)
题解三
我们也可以利用从下玩上的顺序进行求解,即从三角形的最后一层各个顶点出发,最终达到三角形的顶点,求出最短路径。这种接法和题解二刚好相反,必须要正序遍历,不能进行逆序遍历。
int minimumTotal(int** triangle, int triangleSize, int* triangleColSize) {
int *ans = triangle[triangleSize-1];
for(int i = triangleSize-2; i >= 0; i--) {
int *curCol = triangle[i];
for(int j = 0; j < i+1; j++) {
ans[j] = min(ans[j+1], ans[j]) + curCol[j];
}
}
return ans[0];
}
时间复杂度O(n²),空间复杂度O(n)