zoukankan      html  css  js  c++  java
  • 【LeetCode-动态规划】三角形最小路径和

    题目描述

    给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
    示例:

    例如,给定三角形:
    
    [
         [2],
        [3,4],
       [6,5,7],
      [4,1,8,3]
    ]
    自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
    

    题目链接: https://leetcode-cn.com/problems/triangle/

    思路1

    自顶向下动态规划。将三角形画成如下形式更好分析:

    [2],
    [3,4],
    [6,5,7],
    [4,1,8,3]
    

    用rows表示三角形的行数,cols表示三角形的最大列数,dp[i][j]表示从第一行的顶点走到坐标为(i,j)位置的最短路径(rows,cols,i,j均从0开始计数)。分析过程如下

    • 当i=0时,dp[0][0]=triangle[0][0],这是边界值;
    • 当i=1时,因为每一步只能移动到下一行中相邻的结点上,所以当前位置的值取决于上一行的位置以及上一行左边一个位置,也就是dp[1][j]=min(dp[0][j-1], dp[0][j])+triangle[i][j],存在两种特殊情况:
      • 当j=0时,该位置在行的最左边(例如第二行的3),此时dp[i][j]=dp[i-1][j]+triangle[i][j];
      • 当j=rows时,该位置在行的最右边(例如第二行的4),此时dp[i][j]=dp[i-1][j-1]+triangle[i][j];
    • 综上,dp[i][j]=min(dp[i-1][j], dp[i-1][j-1])+triangle[i][j],注意两种特殊情况即可。

    代码如下:

    class Solution {
    public:
        int minimumTotal(vector<vector<int>>& triangle) {
            if(triangle.empty()) return 0;
    
            int rows = triangle.size();
            int cols = triangle[triangle.size()-1].size();
            vector<vector<int>> dp(rows, vector<int>(cols, 0));
            dp[0][0] = triangle[0][0];
            for(int i=1; i<rows; i++){
                for(int j=0; j<=i; j++){
                    if(j==0){
                        dp[i][j] = dp[i-1][j] + triangle[i][j];
                    }else if(j==i){
                        dp[i][j] = dp[i-1][j-1] + triangle[i][j];
                    }else{
                        dp[i][j] = min(dp[i-1][j], dp[i-1][j-1]) + triangle[i][j];
                    }
                }
            }
    
            int minLen = 0x7fffffff;
            for(int i=0; i<dp[rows-1].size(); i++){
                minLen = min(dp[rows-1][i], minLen);
            }
            return minLen;
        }
    };
    
    • 时间复杂度:O(n^2)
    • 空间复杂度:O(n^2)

    改写

    • 状态定义:dp[i][j] 表示从顶点到位置 (i, j) 的最小路径和;
    • 状态转移方程:dp[i][j] = min(dp[i-1][j], dp[i-1][j-1])+triangle[i][j] = min(dp[i-1][j]+triangle[i][j], dp[i-1][j-1]+triangle[i][j]);
    • 边界条件:dp[i][j] 跟 dp[i-1][j] 和 dp[i-1][j-1] 有关, 因为形状是一个三角形,所以我们只有在 i-1>=0 && j<triangle[i-1].size()时,dp[i-1][j]才有效;同理,只有在i-1>=0 && j-1<triangle[i-1].size() 时,dp[i-1][j-1] 才有效。

    代码如下:

    class Solution {
    public:
        int minimumTotal(vector<vector<int>>& triangle) {
            if(triangle.empty()) return 0;
            if(triangle[0].empty()) return 0; 
    
            int rows = triangle.size();
            int cols = triangle[rows-1].size();
            vector<vector<int>> dp(rows, vector<int>(cols, 0));
            for(int i=0; i<rows; i++){
                for(int j=0; j<triangle[i].size(); j++){
                    int a = INT_MAX, b = INT_MAX;
                    if(i-1>=0 && j<triangle[i-1].size()) a = dp[i-1][j] + triangle[i][j];
                    if(j-1>=0 && j-1<triangle[i-1].size()) b = dp[i-1][j-1] + triangle[i][j];
                    dp[i][j] = min(a, b);
                    if(dp[i][j]==INT_MAX) dp[i][j] = triangle[i][j]; // dp[i-1][j], dp[i-1][j-1]均不在范围,对应顶点
                }
            }
    
            int ans = INT_MAX;
            for(int i=0; i<cols; i++) ans = min(dp[rows-1][i], ans);
            return ans;
        }
    };
    

    空间复杂度优化:
    在计算第i行的最短路径时,只需要第i-1行的dp[i-1][j]和dp[i-1][j-1],可以使用两个变量pre和preLeft替代。使用dp[i]表示到第i行的最短路径,代码如下:

    class Solution {
    public:
        int minimumTotal(vector<vector<int>>& triangle) {
            if(triangle.empty()) return 0;
    
            int rows = triangle.size();
            int cols = triangle[triangle.size()-1].size();
            vector<int> dp(cols, 0);
            dp[0] = triangle[0][0];
            int pre = 0, preLeft = 0;   // pre指dp[i-1][j], preLeft指dp[i-1][j-1]
            for(int i=1; i<rows; i++){
                for(int j=0; j<=i; j++){
                    pre = dp[j];
                    if(j==0){
                        dp[j] = pre + triangle[i][j];
                    }else if(j==i){
                        dp[j] = preLeft + triangle[i][j];
                    }else{
                        dp[j] = min(pre, preLeft) + triangle[i][j];
                    }
                    preLeft = pre;
                }
            }
    
            int minLen = 0x7fffffff;
            for(int i=0; i<dp.size(); i++){
                minLen = min(dp[i], minLen);
            }
            return minLen;
        }
    };
    
    • 时间复杂度:O(n^2)
    • 空间复杂度:O(n)

    思路2

    自底向上动态规划。使用dp[i][j]表示包含第i行第j列元素的最短路径,因为dp[i][j]一定会到达dp[i+1][j]和dp[i+1][j+1],所以dp[i][j]=min(dp[i+1][j], dp[i+1][j+1])+triangle[i][j]。
    代码如下:

    class Solution {
    public:
        int minimumTotal(vector<vector<int>>& triangle) {
            if(triangle.empty()) return 0;
    
            int rows = triangle.size();
            int cols = triangle[triangle.size()-1].size();
            vector<vector<int>> dp(rows+1, vector<int>(cols+1, 0));
            for(int i=rows-1; i>=0; i--){
                for(int j=0; j<triangle[i].size(); j++){
                    dp[i][j] = min(dp[i+1][j], dp[i+1][j+1])+triangle[i][j];
                }
            }
            return dp[0][0];
        }
    };
    
    • 时间复杂度:O(n^2)
    • 空间复杂度:O(n^2)

    参考

    1、https://leetcode-cn.com/problems/triangle/solution/javadong-tai-gui-hua-si-lu-yi-ji-dai-ma-shi-xian-b/

  • 相关阅读:
    Oracle新建用户、角色,授权,建表空间
    oracle 创建表空间、创建用户管理该表空间
    后台获取数据,动态更新到网页--无闪烁刷新
    Java获取当前路径
    最实用销售24种技巧(业务必看)转
    四种类型的客户(转)
    800个小炒
    商务称呼礼仪
    人际沟通交流技巧(转)
    拒绝礼仪与技巧(转)
  • 原文地址:https://www.cnblogs.com/flix/p/12747902.html
Copyright © 2011-2022 走看看