动态规划专训2——路径问题

文章介绍了如何使用动态规划方法解决不同路径问题,包括计算从左上角到右下角的路径数量,避开障碍物的路径,珠宝的最大价值,以及下降和最小路径和问题。通过示例代码展示了如何定义状态、转移方程以及填充二维数组来求解这些问题。
摘要由CSDN通过智能技术生成

1.不同路径

62. 不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” 

问总共有多少条不同的路径

因为题给信息,一维数组已经完成不了要求,所以我们要用二维数组

1.状态表示:用dp[ i ][ j ]表示以第 i 行 j 列为结尾的路径总和

2.状态转移方程:dp[ i ][ j ] = dp[ i - 1][ j ] + dp[ i ][ j - 1 ]

3.初始化:dp[0][1] = 1

4.填表顺序:从上往下填每一行,从左往右填每一列

5.返回值:dp[m][n]

class Solution {
public:
    int uniquePaths(int m, int n) 
    {
        //1. 创建 dp 表
        //2. 初始化
        //3. 填表
        //4. 返回值
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        dp[0][1] = 1;

        for(int i = 1; i <= m; ++i)
            for(int j = 1; j <= n; ++j)
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];

        return dp[m][n];
    }
};

这是ac代码

2.不同路径II

63. 不同路径 II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示

1.状态表示:用dp[ i ][ j ]表示以第 i 行 j 列为结尾的路径总和

2.状态转移方程:dp[ i ][ j ] = dp[ i - 1][ j ] + dp[ i ][ j - 1 ]

3.初始化:dp[0][1] = 1

4.填表顺序:从上往下填每一行,从左往右填每一列

5.返回值:dp[m][n]

这题与前面的题相似,要注意当位置为障碍物时,dp[ i ][ j ] 的值应该为0

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) 
    {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();

        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        dp[0][1] = 1;
        for(int i = 1; i <= m ; ++i)
            for(int j = 1; j <= n; ++j)
            {
                if(obstacleGrid[i - 1][j - 1])
                    dp[i][j] = 0;
                else
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }

        return dp[m][n];
    }
};

这是ac代码

3.珠宝的最大价值

LCR 166. 珠宝的最高价值

现有一个记作二维矩阵 frame 的珠宝架,其中 frame[i][j] 为该位置珠宝的价值。拿取珠宝的规则为:

  • 只能从架子的左上角开始拿珠宝
  • 每次可以移动到右侧或下侧的相邻位置
  • 到达珠宝架子的右下角时,停止拿取

1.状态表示:用dp[ i ][ j ]表示以第 i 行 j 列为结尾的最大礼物价值

2.状态转移方程:dp[ i ][ j ] = max( dp[ i - 1][ j ], dp[ i ][ j - 1 ] ) + p[ i ][ j ]

3.初始化

4.填表顺序:从上往下填每一行,从左往右填每一列

5.返回值:dp[m][n]

注意:开vector时每行每列多开一行列,所以找对应p时要注意下标映射关系

class Solution {
public:
    int jewelleryValue(vector<vector<int>>& frame) 
    {
        int m = frame.size();
        int n = frame[0].size();

        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        for(int i = 1; i <= m; ++i)
            for(int j = 1; j <= n; ++j)
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + frame[i - 1][j - 1];

        return dp[m][n];
    }
};

这是ac代码

4.下降路径最小和

931. 下降路径最小和

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

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

1.状态表示:用dp[ i ][ j ]表示以第 i 行 j 列为结尾的下降路径最小和

2.状态转移方程:dp[ i ][ j ] = min( dp[ i - 1][ j ], dp[ i  - 1][ j - 1 ] , dp[ i - 1 ][ j + 1 ]) + p[ i ][ j ]

3.初始化 第一行全为0, 其他全为INT_MAX

4.填表顺序:从上往下填每一行,从左往右填每一列

5.返回值:最后一行的最小值

注意: 下标的映射关系,开数组的大小,返回值的判断

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 j = 0; j < n + 2; ++j)
            dp[0][j] = 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 j = 1; j <= n; ++j)
            ret = min(ret, dp[n][j]);

        return ret;
    }
};

这是ac代码

5.最小路径和

64. 最小路径和

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步

1.状态表示:用dp[ i ][ j ]表示以第 i 行 j 列为结尾的路径最小和

2.状态转移方程:dp[ i ][ j ] = min( dp[ i - 1][ j ], dp[ i ][ j - 1 ]) + p[ i ][ j ]

3.初始化 dp[0][1] = 0, 其他全为INT_MAX

4.填表顺序:从上往下填每一行,从左往右填每一列

5.返回值:dp[m][n]

注意:下标的映射关系,数组的初始化

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) 
    {
        int m = grid.size();
        int n = grid[0].size();

        vector<vector<int>> dp(m + 1, vector<int>(n + 1, INT_MAX));
        dp[0][1] = 0;
        for(int i = 1; i <= m; ++i)
            for(int j = 1; j <= n; ++j)
                dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i - 1][j - 1];

        return dp[m][n];
    }
};

这是ac代码

6.地下城游戏

174. 地下城游戏

恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。

骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。

有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。

为了尽快解救公主,骑士决定每次只 向右 或 向下 移动一步。

返回确保骑士能够拯救到公主所需的最低初始健康点数。

注意:任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间

1.状态表示:用dp[ i ][ j ]表示以第 i 行 j 列为开始的所需最小生命值

2.状态转移方程:dp[ i ][ j ] = min( dp[ i +1][ j ], dp[ i ][ j + 1 ]) - p[ i ][ j ]

3.初始化 dp[m][n - 1] = 0, 其他全为INT_MAX

4.填表顺序:从下往上填每一行,从右往左填每一列

5.返回值:dp[0][0]

注意:初始化和填表顺序,状态转移方程,当dp[ i ][ j ] < 1时,下一个血包很大,导致当前需要可能小于1,我们要对1取max,保证情况的正确性

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) 
    {
        int m = dungeon.size();
        int n = dungeon[0].size();

        vector<vector<int>> dp(m + 1, vector<int>(n + 1, INT_MAX));
        dp[m - 1][n] = 1;
        for(int i = m - 1; i >= 0; --i)
            for(int j = n - 1; j >= 0; --j)
            {
                dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j];
                dp[i][j] = max(dp[i][j], 1);
            }

        return dp[0][0];
    }
};

这是ac代码

新手写博客,有不对的位置希望大佬们能够指出,也谢谢大家能看到这里,让我们一起学习进步吧!

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值