【动态规划】LeetCode-931.下降路径最小和

算法那些事专栏说明:这是一个记录刷题日常的专栏,每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目,在这立下Flag
个人主页:Jammingpro
专栏链接:算法那些事
每日学习一点点,技术累计看得见

题目

题目描述

给你一个 n x n 的 方形 整数数组 matrix ,请你找出并返回通过 matrix 的下降路径 的 最小和 。

下降路径 可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列(即位于正下方或者沿对角线向左或者向右的第一个元素)。具体来说,位置 (row, col) 的下一个元素应当是 (row + 1, col - 1)、(row + 1, col) 或者 (row + 1, col + 1) 。

执行示例

示例 1:
输入:matrix = [[2,1,3],[6,5,4],[7,8,9]]
输出:13
解释:如图所示,为和最小的两条下降路径
【动态规划】LeetCode-931.下降路径最小和_第1张图片

示例2:
输入:matrix = [[-19,57],[-40,-5]]
输出:-59
解释:如图所示,为和最小的下降路径
【动态规划】LeetCode-931.下降路径最小和_第2张图片

提示

n == matrix.length == matrix[i].length
1 <= n <= 100
-100 <= matrix[i][j] <= 100

题解

题目中说到:在下一行选择的元素和当前行所选元素最多相隔一列(即位于正下方或者沿对角线向左或者向右的第一个元素)。这句话说的是,第i行第j列的元素,可以到达第i+1行第j-1列、第i+1行第j列、第i+1行第j+1列元素。如下图左图所示,第0行第1列元素可以到达第1行第0列、第1行第1列、第1行第2列元素。也就是说,当前元素向下走时,可以向左下走、向下走或者向右下走。反过来说,到达第i行第j列的上一步可以是第i-1第j-1列、第i-1行第j列、第i-1行第j+1列,如下图右图所示。
【动态规划】LeetCode-931.下降路径最小和_第3张图片
题目中所说的最小路径是指:从第0行的任意一个元素出发,每一步向左下、向下或向右下走1步,一直到达最后一行,将上述每一步到达的元素相加后,得最小和。我们可以使用一个dp表(二维数组)保存到达第i行第j列的最小路径和。因为第0行是出发点,所以dp[0][i]=matrix[0][i]。如下图所示(下图显示的内容为示例1)↓↓↓
【动态规划】LeetCode-931.下降路径最小和_第4张图片
余下第i行第0列的元素只能从上面一个元素或者从右上元素到达,因为左上元素不存在,故dp[i][0]=min(dp[i-1][j],dp[i-1][j+1])+matrix[i][0]。以示例1为例,计算第1行第0列元素示意图如下↓↓↓
【动态规划】LeetCode-931.下降路径最小和_第5张图片
与下第i行最后一列元素之恶能从上面一个元素或者从左上元素到达,因为右上元素不存在,故dp[i][最后一个元素下标]=min(dp[i-1][j-1],dp[i-1][j])+matrix[i][最后一个元素下标]。以示例1为例,计算第1行最后一个元素示意图如下↓↓
【动态规划】LeetCode-931.下降路径最小和_第6张图片
每一行总非第1个元素和最后一个元素,均可以由左上、上、右上元素向下一步到达,故dp[i][j]=min(dp[i-1][j-1], min(dp[i-1][j], dp[i-1][j+1]))+matrix[i][j]。以示例1为例,计算第1行下标为1的元素示意图如下↓↓
【动态规划】LeetCode-931.下降路径最小和_第7张图片
因为我们计算到达第i行的最小路径和时,需要知道第i-1行的最小路径和,因此,我们的计算需要从上到下。经过上面的分析,我们可以得到如下代码↓↓↓

class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& matrix) {
            int n = matrix.size();
            vector<vector<int>>dp(n, vector<int>(n));
            for(int i = 0; i < n; i++)
                dp[0][i] = matrix[0][i];
            for(int i = 1; i < n; i++)
            {
                dp[i][0] = min(dp[i - 1][0], dp[i - 1][1]) + matrix[i][0];
                dp[i][n - 1] = min(dp[i - 1][n - 2], dp[i - 1][n - 1]) + matrix[i][n - 1];
                for(int j = 1; j < n - 1; j++)
                    dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i - 1][j + 1])) + matrix[i][j];
            }
            int ret = INT_MAX;
            for(int i = 0; i < n; i++)
                ret = min(ret, dp[n - 1][i]);
            return ret;
    }
};

上面代码中需要额外考虑每一行的第1个元素和最后1个元素,还需要额外考虑第1行。我们可以通过对dp表多开辟1行2列,来避免额外考虑上述内容。dp表第0行初始化为0,这样不会影响第1行元素的计算,因为min(dp[i-1][j-1],min(dp[i-1][j],dp[i-1][j+1]))的值始终为0。而第0列和最后一列初始化为INT_MAX(int类型所能表示的最大值),这样在计算min(dp[i-1][j-1],min(dp[i-1][j],dp[i-1][j+1]))时,始终不可能选到INT_MAX所在的那一个元素,因为其数值最大。新开辟的dp表示意图如下,下图标注的※号位置对应于上述代码的dp表↓↓↓
【动态规划】LeetCode-931.下降路径最小和_第8张图片
通过dp增开1行2列后的代码如下,其代码行数有所缩减↓↓↓

class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& matrix) {
        int n = matrix.size();
        vector<vector<int>>dp(n + 1, vector<int>(n + 2, INT_MAX));
        for(int i = 0; i <= n + 1; i++)
            dp[0][i] = 0;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i - 1][j + 1])) + matrix[i - 1][j - 1];
        int ret = INT_MAX;
        for(int i = 1; i <= n; i++)
            ret = min(ret, dp[n][i]);
        return ret;
    }
};

本文存在不足,欢迎留言或私信批评、指正。希望我的解决方法能够对你有所帮助~~
今日打卡完成,点亮小星星☆→★

你可能感兴趣的:(算法那些事,动态规划,leetcode,算法)