109 数字三角形

原题网址:https://www.lintcode.com/zh-cn/problem/triangle/

描述

给定一个数字三角形,找到从顶部到底部的最小路径和。每一步可以移动到下面一行的相邻数字上。

如果你只用额外空间复杂度O(n)的条件下完成可以获得加分,其中n是数字三角形的总行数。

您在真实的面试中是否遇到过这个题?  

样例

比如,给出下列数字三角形:

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

从顶到底部的最小路径和为11 ( 2 + 3 + 5 + 1 = 11)。

标签
动态规划(DP)
 
思路:首先明确“每一步可以移动到下面一行的相邻数字上。”是什么意思,即是说当前行的某列 j,可以选择移动到下一行的 j 或者 j+1 。【注意图中的三角形状,当前行的 j 与下一行的 j-1 不相邻。最开始没明白这点,汗-_-||】
 
可以建立一个二维数组dp[n][n]表示终点为元素(i,j)的最小路径和,到达(i,j)要么从(i-1,j)走,要么从(i-1,j-1)走。

则dp【i】【j】为上一行的同列和前一列路径和小的加上triangle【i】【j】,即dp【i】【j】=min(dp【i-1】【j】,dp【i-1】【j-1】)+triangle【i】【j】。

初始dp【0】【0】=triangle【0】【0】。

关于防止数组下标越界问题:转自此博客

//要取dp[i-1][j-1],和dp[i-1][j],但是要保证不越界。

//第i-1行的j的取值范围为[0, i-1]

int lo = max(0, j-1);//当j-1小于0的时候只取0,大于等于0的时候取自身。

int hi = min(j, i-1);//当j大于i-1的时候只取i-1,小于等于i-1的时候取自身。

代码:

int minimumTotal(vector<vector<int>> &triangle)
{
    int n=triangle.size();
    if (n==0)
    {
        return 0;
    }
    vector<vector<int>> dp(n,vector<int>(n,0));
    dp[0][0]=triangle[0][0];
    for (int i=1;i<n;i++)
    {
        for (int j=0;j<=i;j++)
        {
            //防止访问上一行时列下标越界;
            //第i-1行j的取值范围为0~i-1;
            int lo=max(0,j-1); //lo最小值为0;
            int hi=min(j,i-1);  //hi最大值为i-1;
            dp[i][j]=min(dp[i-1][lo],dp[i-1][hi])+triangle[i][j];
        }
    }
    int result=dp[n-1][0];
    for (int i=1;i<n;i++)
    {
        if (dp[n-1][i]<result)
        {
            result=dp[n-1][i];
        }
    }
    return result;
}

如果只用额外空间复杂度O(n),可把行信息去掉,定义一维数组dp[n],dp[j] 表示遍历到第 i 行时,三角形中第 i 行第 j 列处为终点的最小路径和。

状态转移方程为 dp[j] = min(dp[j] (上一行)+ dp[j-1](上一行))+ triangle[i][j]。

注意根据状态方程更新数据时要考虑覆盖问题:如果对列从前往后遍历,则上一行相应dp值被当前行dp值覆盖,影响下一列的计算,所以应从后往前遍历。可参考 此博客 及 背包问题 。

遍历完三角形后,dp[n]中存放的是从顶点到最后一行各点的最小路径和,找出其中的最小值return出去即可。

AC代码:

class Solution {
public:
    /**
     * @param triangle: a list of lists of integers
     * @return: An integer, minimum path sum
     */
    int minimumTotal(vector<vector<int>> &triangle) {
        // write your code here
    int n=triangle.size();
    if (n==0)
    {
        return 0;
    }
    vector<int> dp(n,0);
    dp[0]=triangle[0][0];
    for (int i=1;i<n;i++)
    {
        for(int j=i;j>=0;j--) //注意这里的顺序,如果从前向后遍历将覆盖上一行相应位置的最短路径,所以应该从后向前遍历;
        {
            //防止访问上一行时列下标越界;
            int lo=max(0,j-1); //lo最小值为0;
            int hi=min(j,i-1);  //hi最大值为i-1;
            dp[j]=min(dp[lo],dp[hi])+triangle[i][j];
        }
    }
    int result=dp[0];
    for (int i=1;i<n;i++)
    {
        if (dp[i]<result)
        {
            result=dp[i];
        }
    }
    return result;
    }
};

其它参考:

https://blog.csdn.net/this_is_qiqi/article/details/78388888

猜你喜欢

转载自www.cnblogs.com/Tang-tangt/p/9130608.html
今日推荐