首页 > 编程语言 >Offer必备算法13_路径dp_六道力扣题详解(由易到难)

Offer必备算法13_路径dp_六道力扣题详解(由易到难)

时间:2024-03-13 13:05:25浏览次数:31  
标签:13 Offer int 由易到难 路径 示例 vector public dp

目录

①力扣62. 不同路径

解析代码

②力扣63. 不同路径 II

解析代码

③力扣LCR 166. 珠宝的最高价值

解析代码

④力扣931. 下降路径最小和

解析代码

⑤力扣64. 最小路径和

解析代码

⑥力扣174. 地下城游戏

解析代码

本篇完。


①力扣62. 不同路径

62. 不同路径

难度 中等

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

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

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

示例 1:

输入:m = 3, n = 7
输出:28

示例 2:

输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下

示例 3:

输入:m = 7, n = 3
输出:28

示例 4:

输入:m = 3, n = 3
输出:6

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 10^9
class Solution {
public:
    int uniquePaths(int m, int n) {

    }
};

解析代码

dp[i][j] 表示:到 [i, j] 位置处,⼀共有多少种方式。状态转移方程为dp[i][j] = dp[i-1][j] + dp[i][j-1];

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> dp(m+1, vector<int>(n+1, 0)); // 多开一行一列
        dp[0][1] = 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];
    }
};

②力扣63. 不同路径 II

63. 不同路径 II

难度 中等

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

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

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

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

示例 1:

输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

示例 2:

输入:obstacleGrid = [[0,1],[0,0]]
输出:1

提示:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j] 为 0 或 1
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {

    }
};

解析代码

本题为力扣62不同路径的变型,只不过有些地方有障碍物,只要在状态转移上稍加修改就可解决。

  • dp[i][j] 表示:到 [i, j] 位置处,一共有多少种方式。

状态转移方程:

  • 如果不是障碍物:dp[i][j] = dp[i-1][j] + dp[i][j-1];
  • 如果是障碍物:dp[i][j] = 0;
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size(), n = obstacleGrid[0].size();
        // dp[i][j] 表示:到 [i, j] 位置处,⼀共有多少种方式。
        vector<vector<int>> dp(m+1, vector<int>(n+1, 0)); // 多开一行一列
        dp[0][1] = 1; // 虚拟结点,保证后面填表正确(让第一行第一列都是1)
        for(int i = 1; i <= m; ++i)
        {
            for(int j = 1; j <= n; ++j)
            {
                if(obstacleGrid[i-1][j-1] == 0) // 如果不是障碍物
                    dp[i][j] = dp[i-1][j] + dp[i][j-1];
                // 如果是障碍物,dp[i][j]是0,本来就初始成0,不用处理
            }
        }
        return dp[m][n];
    }
};

③力扣LCR 166. 珠宝的最高价值

LCR 166. 珠宝的最高价值

难度 中等

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

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

注意:珠宝的价值都是大于 0 的。除非这个架子上没有任何珠宝,比如 frame = [[0]]

示例 1:

输入: frame = [[1,3,1],[1,5,1],[4,2,1]]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最高价值的珠宝

提示:

  • 0 < frame.length <= 200
  • 0 < frame[0].length <= 200
class Solution {
public:
    int jewelleryValue(vector<vector<int>>& frame) {

    }
};

解析代码

dp[i][j] 表示:到 [i, j] 位置处,此时的最大价值。

状态转移方程为dp[i][j] = frame[i-1][j-1] + max(dp[i-1][j], dp[i][j-1]);

class Solution {
public:
    int jewelleryValue(vector<vector<int>>& frame) {
        int m = frame.size(), n = frame[0].size();
        // dp[i][j] 表示:到 [i, j] 位置处,此时的最大价值
        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] = frame[i-1][j-1] + max(dp[i-1][j], dp[i][j-1]);
            }
        }
        return dp[m][n];
    }
};

④力扣931. 下降路径最小和

931. 下降路径最小和

难度 中等

给你一个 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
解释:如图所示,为和最小的两条下降路径

示例 2:

输入:matrix = [[-19,57],[-40,-5]]
输出:-59
解释:如图所示,为和最小的下降路径

提示:

  • n == matrix.length == matrix[i].length
  • 1 <= n <= 100
  • -100 <= matrix[i][j] <= 100
class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& matrix) {

    }
};

解析代码

dp[i][j] 表示:到 [i, j] 位置处,此时下降路径最小和。

状态转移方程是dp[i][j] = matrix[i-1][j-1] + min(min(dp[i-1][j], dp[i-1][j-1]), dp[i-1][j+1]);

要注意初始化:

class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& matrix) {
        int n = matrix.size();
        // dp[i][j] 表示:到 [i, j] 位置处,此时下降路径最小和
        // 初始化,保证填表正确,初始化成INT_MAX是为了不影响第1列和最后1列的比较大小
        vector<vector<int>> dp(n+1, vector<int>(n+2, INT_MAX)); // 多开一行两列
        for(int i = 0; i < n+2; ++i) // 这里初始化成0是为了不影响第一行的值
        {
            dp[0][i] = 0;
        }

        for(int i = 1; i <= n; ++i)
        {
            for(int j = 1; j <= n; ++j)
            {
                dp[i][j] = matrix[i-1][j-1] + min(min(dp[i-1][j], dp[i-1][j-1]), dp[i-1][j+1]);
            }
        }

        int ret = INT_MAX;
        for(int i = 1; i <= n; ++i)
        {
            ret = min(dp[n][i], ret);
        }
        return ret;
    }
};

⑤力扣64. 最小路径和

64. 最小路径和

 难度 中等

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

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

示例 1:

输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。

示例 2:

输入:grid = [[1,2,3],[4,5,6]]
输出:12

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 200
  • 0 <= grid[i][j] <= 200
class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {

    }
};

解析代码

和之前的题类似,只是初始化要改一改。

dp[i][j] 表示:到 [i, j] 位置处,此时的最小路径和。

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

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        // dp[i][j] 表示:到 [i, j] 位置处,此时的最小路径和
        // 初始化,保证填表正确,初始化成INT_MAX是为了不影响第1列和最后1列的比较大小
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, INT_MAX)); // 多开一行一列
        dp[0][1] = dp[1][0] = 0; // 初始化保证填表正确,原来填了INT_MAX

        for(int i = 1; i <= m; ++i)
        {
            for(int j = 1; j <= n; ++j)
            {
                dp[i][j] = grid[i-1][j-1] + min(dp[i-1][j], dp[i][j-1]);
            }
        }

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

⑥力扣174. 地下城游戏

174. 地下城游戏

难度 困难

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

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

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

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

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

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

示例 1:

输入:dungeon = [[-2,-3,3],[-5,-10,1],[10,30,-5]]
输出:7
解释:如果骑士遵循最佳路径:右 -> 右 -> 下 -> 下 ,则骑士的初始健康点数至少为 7 。

示例 2:

输入:dungeon = [[0]]
输出:1

提示:

  • m == dungeon.length
  • n == dungeon[i].length
  • 1 <= m, n <= 200
  • -1000 <= dungeon[i][j] <= 1000
class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {

    }
};

解析代码

        路径dp问题,这道题的状态表示如果定义成:从起点开始,到达 [i, j] 位置的时候,所需的最低初始健康点数。 那么分析状态转移方程的时候会有一个问题:那就是当前的健康点数还会受到后面的路径的影响。也就是从上往下的状态转移不能很好地解决问题。

        这个时候我们要换⼀种状态表示:从 [i, j] 位置出发,到达终点时所需要的最低初始健康点 数。这样在分析状态转移的时候,后续的最佳状态就已经知晓。

综上所述, dp[i][j] 表示:从 [i, j] 位置出发,到达终点时所需的最低初始健康点数。


状态转移方程:

dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j];

        如果当前位置的 dungeon[i][j] 是⼀个比较大的正数的话, dp[i][j] 的值可能变 成 0 或者负数。也就是最低点数会小于 1 ,那么骑士就会死亡。因此求出来的 dp[i] [j] 如果小于等于 0 的话,说明此时的最低初始值应该为 1 。


初始化和返回值:

        在本题中,在 dp 表最后面添加一行,并且添加一列后,所有的值都先初始化为INT_MAX,然后让 dp[m][n - 1] = dp[m - 1][n] = 1 即可。

从右下往左上填值,最后返回 dp[0][0] 的值。

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][n-1] = 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];
                if(dp[i][j] < 1)
                    dp[i][j] = 1;
            }
        }
        return dp[0][0];
    }
};

本篇完。

下一篇是有关哈希表的OJ,下下篇是简单多状态dp类型的动态规划。

标签:13,Offer,int,由易到难,路径,示例,vector,public,dp
From: https://blog.csdn.net/GRrtx/article/details/136508974

相关文章

  • 算法入门书籍(二)--2024.03.13
    小学C++编程入门书籍及相关资料介绍(二)算法篇小学C++编程入门书籍及相关资料介绍(二)算法篇_c++教材-CSDN博客 算法入门书籍--2022.04.04算法入门书籍--2022.04.04-CSDN博客1、聪明人的游戏信息学探秘.提高篇-2017年06月2、啊哈!算法3、哇,编程!——跟小明一起学......
  • 【3月13日-云服务器推荐】京东云新上1年福利机型 阿里云活动页大改 选购指南 最新价格
     本文纯原创,侵权必究 《最新对比表》已更新在文章头部—腾讯云文档,文章具有时效性,请以腾讯文档为准!【腾讯文档实时更新】云服务器1分钟教会你如何选择教程https://docs.qq.com/document/DV0RCS0lGeHdMTFFV?tab=000003视频解读最新活动:【云服务器推荐】价格对比!阿里云......
  • 3月13每日打卡
    净现值计算的程序JAVAimportjava.util.Scanner;publicclassNPVCalculator{publicstaticvoidmain(String[]args){Scannerinput=newScanner(System.in);System.out.print("请输入投资项目的年限:");intyears=input.nextInt();......
  • 就业寒冬,最终拿到5个offer的一些经验分享
    本人是霍格沃兹测试开发学社深圳线下1期学员,拥有5年测试工作经验,之前一直在某大厂做软件测试,期间做过测试执行、带过外包团队到负责大型项目的质量交付。经过一个半月的高强度找工作奋战,最终拿下了5家公司offer,选择了一家自己很满意的公司。准备过程前期准备是很迷茫的,因为面试......
  • 今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 3月13日,星期三
    每天一分钟,知晓天下事!2024年3月13日星期三农历二月初四1、四部门:中小学每天安排30分钟大课间体育活动,缓解视力疲劳。2、我国视障人士首次使用无障碍格式文件完成结婚登记。3、数据显示:城乡居民医保的参保人数从2019年开始逐渐下降。4、福建:节假日期间,鼓励......
  • 13-网络编程
    在网络通信协议下,不同计算机上运行的程序进行数据的传输;封装在java.net包下网络编程三要素:IP:设备在网络中的地址,是唯一的标识端口号:应用程序在设备中的唯一标识协议:数据在网络中传输的规则TCP、UDP、http、https、ftpIP:InternetProtocol互联网协议地址,常见的IP分为IPV4......
  • 3月13日 新榜情报
    3月13日新榜情报1.“抖音商城38好物节”日均支付GMV同比增长33%2.抖音电商推出“抖音商城服饰焕新周”专项促销活动3.快手电商“兴农计划”首站直播培训落地海南三亚4.快手:2023年向16.1万名用户发送网暴提醒或不友善言论提醒5.小红书上线商家举报中心6.诺贝尔文学奖得主古......
  • [js error] SyntaxError: Unexpected token ‘{‘ (at uniFile.js?t=1710138723630:1:
    问题详情问题描述封装一个函数的时候报错问题原因SyntaxError:Unexpectedtoken‘{’(atuniFile.js?t=1710138723630:1:34)SyntaxError:意外的令牌“{”(在uniFile.js?t=1710138723630:1:34)意思是有不符合语法规范的地方在第一行34个字符的地方去到报错文件的地方查......
  • 破链成环-acwing第131场周赛-奶牛报数
    5364.奶牛报数-AcWing题库有 n 头奶牛,围成一圈,顺时针依次编号为 1∼n。其中,第 i 头奶牛的重量为 ai。现在,我们需要选择一头奶牛,并从该奶牛开始,所有奶牛按照顺时针的顺序进行 1∼n报数。报数完毕后,所有报出的数在 [l,r)范围内的奶牛,会被选中制作牛肉。我们希......
  • OMAP-L138 + FPGA开发板(Pin-To-Pin国产FPGA)
    XQ138F-EVM是广州星嵌电子科技有限公司基于TIOMAP-L138(定点/浮点DSPC674x+ARM9)+FPGA处理器研制的开发板;OMAP-L138选用TI德州仪器的TMS320C6748+ARM926EJ-S异构双核处理器,主频456MHz,高达3648MIPS和2746MFLOPS的运算能力;FPGA采用中科亿海微eHiChip6家族EQ6......