今日签到题,题目如下:
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。
例如,给定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。来源:力扣(LeetCode)说明:
如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。
链接:https://leetcode-cn.com/problems/triangle
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
动态规划做多了真的是很容易联想到动态规划了。
自顶而下的最小路径和,也可以理解为自底而上的最小路径和。每个节点有两个子节点,只选择路径和最短的那个子节点移动。即每个节点的路径和状态转移自子节点的路径和。
假设父节点的下标为 i ,则其子节点下标为 i 和 i + 1,所以只需要建立一个长度为 n(n 为三角形的总行数)的 int 数组 dp 保存状态(因为更新 dp[i] 时,状态只转移自 dp[i] 和 dp[i+1],更新 dp[i-1] 不影响其状态)。
边界为最底层,先遍历最底层存入 dp。
右下至上从最下层第二行遍历整个三角形,根据上述状态转移规则,表达式为 dp[i] = dp[i] < dp[i+1]?dp[i]:dp[i+1]。完成状态转移后,还需要加上本格路径,表达式为 dp[i] += triangle[j][i]。最后收束到根节点即 dp[0] 为答案。
复杂度分析:
遍历了整个三角形,时间复杂度为 O(N^2)。
使用一个长度为 n 的额外数组,空间复杂度为 O(N)。
以下为自己提交的代码:
public class Solution {
public int MinimumTotal(IList<IList<int>> triangle) {
if (triangle.Count < 1)
{
return 0;
}
int[] dp = new int[triangle.Count];
for (int i = triangle.Count - 1; i >= 0; i--)
{
dp[i] = triangle[triangle.Count - 1][i];
}
for (int i = triangle.Count - 2; i >= 0; i--)
{
for(int j = 0; j <= i; j++)
{
dp[j] = dp[j] < dp[j + 1] ? dp[j]:dp[j + 1];
dp[j] += triangle[i][j];
}
}
return dp[0];
}
}
以上避免越界我先进行了一次判断和一次循环赋值。看完题解之后法线,只需要令 dp 多申请一位就可以优化写法。因为数组 dp 初始化每位都为 0,所以可以作为边界的状态转移。多申请一维避免边界的最后一位状态转移时越界。
复杂度同上。
以下为自己提交的代码:
public class Solution {
public int MinimumTotal(IList<IList<int>> triangle) {
int[] dp = new int[triangle.Count + 1];
for (int i = triangle.Count - 1; i >= 0; i--)
{
for(int j = 0; j <= i; j++)
{
dp[j] = dp[j] < dp[j + 1] ? dp[j]:dp[j + 1];
dp[j] += triangle[i][j];
}
}
return dp[0];
}
}