持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第22天,点击查看活动详情
给定一个三角形 triangle ,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。
示例 1:
输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
2
3 4
6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
示例 2:
输入:triangle = [[-10]]
输出:-10
动态规划
我们用 表示从三角形顶部走到位置 的最小路径和。这里的位置 指的是三角形中第 行第 列(均从 0 开始编号)的位置。
由于每一步只能移动到下一行「相邻的节点」上,因此要想走到位置 ,上一步就只能在位置 或者位置 。我们在这两个位置中选择一个路径和较小的来进行转移,状态转移方程为:
其中 表示位置 对应的元素值。
注意第 行有 个元素,它们对应的 的范围为 。当 或 时,上述状态转移方程中有一些项是没有意义的。例如当 时, 没有意义,因此状态转移方程为:
即当我们在第 行的最左侧时,我们只能从第 行的最左侧移动过来。当 时, 没有意义,因此状态转移方程为:
即当我们在第 行的最右侧时,我们只能从第 行的最右侧移动过来。
最终的答案即为 到 中的最小值,其中 是三角形的行数。
const minimumTotal = (triangle) => {
const h = triangle.length;
const dp = new Array(h);// 初始化状态数组
for (let i = 0; i < h; i++) {
dp[i] = new Array(triangle[i].length);
}
for (let i = h - 1; i >= 0; i--) { //从三角形底向上上遍历
for (let j = 0; j < triangle[i].length; j++) { //遍历同层
if (i == h - 1) { //基本情况 三角形最下一层的状态
dp[i][j] = triangle[i][j];
} else { // 状态转移方程,从下一层相邻位置选取一个较小者 + 自身
dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle[i][j];
}
}
}
return dp[0][0];
};
//状态压缩
const minimumTotal = (triangle) => {
const bottom = triangle[triangle.length - 1];
const dp = new Array(bottom.length);
for (let i = 0; i < dp.length; i++) {//最后一行
dp[i] = bottom[i];
}
for (let i = dp.length - 2; i >= 0; i--) {// 从倒数第二列开始迭代
for (let j = 0; j < triangle[i].length; j++) {
dp[j] = Math.min(dp[j], dp[j + 1]) + triangle[i][j];
}
}
return dp[0];
};