动态规划|三角形最小路径和(力扣120)

本题思路和动态规划|最小路径和(力扣64)-CSDN博客类似。

问题描述:120. 三角形最小路径和 - 力扣(LeetCode)

给定一个三角形 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

提示:

  • 1 <= triangle.length <= 200
  • triangle[0].length == 1
  • triangle[i].length == triangle[i - 1].length + 1
  • -104 <= triangle[i][j] <= 104

问题分析

由于每一步只能移动到下一行中相邻的结点上(相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点),所以每行的第一个元素(即triangle[i][0])只能从第一行的第一个元素开始向下移动到达(红色箭头);每行的最后一个元素(即triangle[i][i])只能从第一行的第一个元素开始向右下移动到达(蓝色箭头)。在这两种情况下,路径是唯一的,因此每个元素对应的最小路径和即为对应的路径上的数字总和。

对于其他元素,可以从其上方相邻元素向下移动一步到达,或者从其左上方相邻元素向右下移动一步到达(绿色箭头),元素对应的最小路径和等于其上方相邻元素与其左上方相邻元素两者对应的最小路径和中的最小值加上当前元素的值。

设dp[i][j]为到达triangle[i][j]的最小路径和,经上述分析,可得如下方程:

假设triangle有n行,则自顶向下的最小路径和即为dp中末行元素dp[i-1]中的最小值。

C++代码

#include<bits/stdc++.h>
using namespace std;

typedef vector<int> vint;
typedef vector<vint> vvint;

int minimumTotal(vector<vector<int>>& triangle) {
    int n = triangle.size(), res = INT_MAX;
    vvint dp(n, vint(n));
    dp[0][0] = triangle[0][0];
    for (int i = 1; i < n; i++) {
        for (int j = 0; j <= i; j++) {
            int k = triangle[i][j];
            if (j == 0) {
                dp[i][j] = dp[i - 1][j] + k;
            } else if (j == i) {
                dp[i][j] = dp[i - 1][j - 1] + k;
            } else {
                dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j]) + k;
            }
        }
    }
    for (int i = 0; i < n; i++) {
        if (res > dp[n - 1][i]) {
            res = dp[n - 1][i];
        }
    }
    return res;
}

int main() {
    vvint triangle = {{-1},{-2,-3}};
    int res = minimumTotal(triangle);
    cout << res << endl;
    return 0;
}

复杂度分析

时间复杂度:使用了双层for循环,故时间复杂度为O(n^2)

空间复杂度:使用二维数组dp存储状态,故空间复杂度为O(n^2)

将空间复杂度降为O(n)

观察上述状态转移方程,可以发现dp[i]只与dp[i-1]有关,因此可以将dp改为一维数组,从而将空间复杂度降为O(n)

在新的方程中,等式右侧表示上一行(即第i-1行)的情况,左侧表示本行(即第i行)的情况。

因此,代码可优化为:

#include<bits/stdc++.h>
using namespace std;

typedef vector<int> vint;

int minimumTotal(vector<vector<int>>& triangle) {
    int n = triangle.size(), res = INT_MAX;
    vint dp(n);
    dp[0] = triangle[0][0];
    for (int i = 1; i < n; i++) {
        /* 下一句代码中:
        * dp[i-1]代表到达triangle[i-1][i-1]的最小路径
        * dp[i]代表到达triangle[i][i]的最小路径
         */
        dp[i] = dp[i - 1] + triangle[i][i];
        // 开始处理第i行, dp[j]表示到达triangle[i][j]的最小路径
        // 处理dp[j]时, dp[k](k > j)已被处理, 故从j从i-1开始递减枚举
        for (int j = i - 1; j >= 0; j--) {
            int k = triangle[i][j];
            if (j == 0) {
                dp[j] = dp[j] + k;
            } else {
                dp[j] = min(dp[j - 1], dp[j]) + k;
            }
        }
    }
    for (int i = 0; i < n; i++) {
        if (res > dp[i]) {
            res = dp[i];
        }
    }
    return res;
}

int main() {
    vvint triangle = {{2},{3,4},{5,6,7},{4,1,8,3}};
    int res = minimumTotal(triangle);
    cout << res << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值