【动态规划】dp 路径问题(不同路径、路径最小和、地下城游戏...)

1. 前言 - 理解动态规划算法

关于 动态规划的理解 与例题,点击👇

【动态规划】C++解决斐波那契模型题目(三步问题、爬楼梯、解码方法…)

有了上面的经验,我们来解下面 dp的路径问题

1.5 关于dp路径问题

  • 在路径问题中,通常需要找到从起点到终点的一条路径,使得路径满足一定的约束条件(如最短路径、最大价值路径等)。

  • 在动态规划中,通常采用一个二维数组或类似的数据结构来存储中间状态,其中每个状态表示从起点到当前位置的某种信息(如路径长度、路径价值等)。

在这里插入图片描述


2. 例题

2.1_不同路径

在这里插入图片描述

思路

  1. 首先 找状态表示 状态转移方程

在这里插入图片描述

Warning. 关于状态表示

实际上在分析状态表示 时,一般会考虑两种表示法

  1. 以(i, j)位置为终点 时的count
  2. 以(i, j)位置为起点 到终点时的count

在本章算法题中,仅最后一道题会涉及到两种表示法,其余的题以第一种即可解题。


  1. 随后进行 对表中内容的初始化 (以及细节问题:填表顺序、返回值)

在这里插入图片描述

代码

class Solution {
public:
    int uniquePaths(int m, int n) {
        // 创建dp表
        vector<vector<int>> dp(m + 1, vector<int>(n + 1)); // 虚拟空间

        // 填写虚拟空间(默认为0!!)
        // for(int i = 0; i <= m; ++i) dp[0][i] = 0;
        // for(int j = 0; j <= n; ++j) dp[j][0] = 0;
        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];
    }
};

3. 算法题


3.1_不同路径II

在这里插入图片描述

思路

读题后,我们发现本题比起之前的,仅仅加了障碍物的限制。

  • 所以我们只需要在填表时加一句判断即可,当(i, j)位置不为障碍物时,进行填表dp[i][j]。
    • 为什么?
      • 当(i, j)为障碍物,dp[i][j]为0,不做统计。

代码

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size(), n = obstacleGrid[0].size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1)); // 初始化dp数组(虚拟空间+1行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] == 0) // 虚拟空间多加了一行一列,找初始矩阵时,映射下标要-1
                    dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }

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

3.2_珠宝的最高价值

在这里插入图片描述

思路

  1. 首先 找状态表示 状态转移方程

在这里插入图片描述

  1. 随后进行 对表中内容的初始化 (以及细节问题:填表顺序、返回值)

在这里插入图片描述

代码

class Solution {
public:
    int jewelleryValue(vector<vector<int>>& frame) {
        int m = frame.size(), n = frame[0].size();
        // dp的创建 与 元素初始化
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

        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]; // 映射下标(对于frame要-1)

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

3.3_下降路径最小和

在这里插入图片描述

思路

不再过多解释,直接跟着思路五步走:

在这里插入图片描述

在这里插入图片描述

代码

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 + 1; ++j) dp[0][j] = 0; // 将第一行初始化为0
        
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= n; ++j)
            {
                // 映射下标,matrix下标-1
                dp[i][j] = min(min(dp[i-1][j], dp[i-1][j-1]), 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;
    }
};

3.4_最小路径和

在这里插入图片描述

思路

  • 题目分析:本题不再画图,根据题目可以看出来和《珠宝的最高价值》一题是极为相似的,需要注意的是初始化时虚拟空间值的设置。

代码

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, INT_MAX)); // 虚拟空间+1行1列。初始化为无穷大
        dp[0][1] = dp[1][0] = 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]; // grid 映射下标(需要-1)

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


3.5_地下城游戏

在这里插入图片描述

思路

关于状态表示的两种选法:

下面介绍了,为什么对于本题我们无法继续以(i, j)位置为终点,找健康点数
在这里插入图片描述
设置了正确的状态表示才能写出正确的状态转移方程:

在这里插入图片描述
最后进行内容初始化以及其余细节问题:

在这里插入图片描述

代码

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        int m = dungeon.size(), n = dungeon[0].size();
       // dp[i][j]: 以(i, j)位置开始,到达终点的 最低初始健康点数
        // 右侧下侧扩充一行一列
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, INT_MAX));
        dp[m-1][n] = dp[m][n-1] = 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(1, dp[i][j]); // 防止负数血量的出现
            }

        return dp[0][0];
    }
};
  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 矩阵最小路径问题是指给定一个矩阵,从左上角出发,每次只能向右或向下走,到达右下角的最小路径和。这个问题可以使用动态规划法来解决。具体来说,我们可以定义一个二维数组dp,其中dp[i][j]表示从左上角到达(i,j)位置的最小路径和。则有以下状态转移方程: dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + matrix[i][j] 其中,matrix[i][j]表示矩阵中第i行第j列的元素值。最终的答案即为dp[m-1][n-1],其中m和n分别为矩阵的行数和列数。 ### 回答2: 矩阵最小路径问题指的是在一个矩阵中,从左上角到右下角的一条路径上,求路径上所有元素值之和最小的问题。这是一个典型的动态规划问题。 我们可以定义一个二维数组dp[m][n],其中dp[i][j]表示从矩阵的左上角到第i行j列的元素的最小路径和。显然,矩阵的左上角元素的最小路径和为其本身,即dp[0][0] = matrix[0][0]。 对于其他位置的元素,我们可以通过以下递推公式来求解: dp[i][j]=min(dp[i-1][j],dp[i][j-1])+matrix[i][j] 其中,dp[i-1][j]表示从左上角到dp[i-1][j]的路径最小和,dp[i][j-1]表示从左上角到dp[i][j-1]的路径最小和,matrix[i][j]表示当前位置的元素值。 最后,dp[m-1][n-1]就是从左上角到右下角的最小路径和。 具体的实现可以用两层循环来遍历矩阵,来更新dp数组。最终的时间复杂度为O(mn),空间复杂度也为O(mn)。 需要注意的是,在实际的求解过程中,为了避免dp数组中的元素被反复读取,可以在原始矩阵上进行原地修改,不需要新建一个dp数组。这样可以将空间复杂度降至O(1)。 总之,矩阵最小路径问题是一个经典的动态规划问题。通过定义dp数组,以及递推公式,可以很好地解决这个问题。在实际中,需要注意如何优化空间复杂度,以及边界条件的处理。 ### 回答3: 矩阵最小路径问题是指在一个矩阵中,从左上角走到右下角,每次只能向下或向右走,求经过的路径上数字之和的最小值。这个问题可以使用动态规划算法来解决。 首先,我们可以定义一个二维的状态数组 dp[i][j],其中 dp[i][j] 表示从左上角走到位置 (i,j) 的最小路径和。对于第一行和第一列,它们只能向右或向下走,因此它们的状态值可以直接累加。即 dp[i][0] = sum(0,0->i,0),dp[0][j] = sum(0,0->0,j)。 对于其它位置 (i,j),它们可以从其上面或左边的位置转移而来。因此,我们有如下的转移方程: dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + matrix[i][j] 其中,matrix[i][j] 表示位置 (i,j) 上的数字。 最后,矩阵最小路径和就是 dp[m-1][n-1],其中 m 和 n 分别是矩阵的行数和列数。 使用动态规划算法求解矩阵最小路径问题的时间复杂度为 O(mn),空间复杂度为 O(mn)。因此,这个算法在面对大规模矩阵时可能会产生性能问题。为了解决这个问题,我们可以使用滚动数组来减小空间复杂度,或者使用贪心算法来近似解决问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值