[动态规划]---part2

news2024/11/25 4:39:23

前言

作者:小蜗牛向前冲

专栏小蜗牛算法之路

 专栏介绍:"蜗牛之道,攀登大厂高峰,让我们携手学习算法。在这个专栏中,将涵盖动态规划、贪心算法、回溯等高阶技巧,不定期为你奉上基础数据结构的精彩算法之旅。一同努力,追逐技术的星辰大海。"

 

目录

一、不同路径II(medium)

a、解题思路 

b、代码

二、礼物的最⼤价值(medium)

a、解题思路 

b、代码

三、 下降路径最⼩和(medium)

a、解题思路 

b、代码

四、 最⼩路径和(medium)

a、解题思路 

b、代码

五、地下城游戏(hard) 

a、解题思路 

b、代码


本期:继续手撕动态规划:不同路径II(medium),礼物的最⼤价值(medium),下降路径最⼩和(medium),最⼩路径和(medium),地下城游戏(hard)

继续刷动态规划相关算法题,如果不清楚什么是动态规划的可以看这里:[动态规划]---part1

一、不同路径II(medium)

一个机器人位于一个 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) {

    }
};

a、解题思路 

这道题和同路径I的解题思路非常相似,但是就是在机器人找 到终点的过程中,会有障碍物。

1、转态表示

首先我们想以i,j位置为结尾表示什么

dp[i][j表示:以i,j位置结尾的时候,机器人到这里有多少条路径

 2、状态转移方程

根据最近的一个位置划分:

当前位置有障碍物:

返回0

当前位置没有障碍物:

dp[i][j] = dp[i-1][j]+dp[i][j-1];

大家可能会想,我要是在[i-1][j]或者[i][j-1]遇到障碍物了,状态转态方程还可以相加吗?

其实是可能的,因为我在遇到障碍物是返回0的,而0对相加的结果是没有影响的。 

3、初始化

这里我们要初始化,就是在二维数组多开一行和一列,但我们要思路多开的行列填什么呢(一切都是为了填表走服务)?,很明显,机器人是在[1,1]位置开始走的,也就说[1,1]位置肯定要为1(当最特色情况起点就是中点),所以我们只要保证,dp[1][0]==1或者dp[0][1]==1即可。其他位置看情况决定。

这里除了要注意填表的初始化,还要注意下标映射关心的改变:

 obstacleGrid[0][0]-------->映射dp[1][1];

 obstacleGrid[2][3]-------->映射dp[3][4];

也就是横着纵坐标都要加1

4、 填表顺序

从上往下填写每一行,每一行都是从左往又开始填写 

5、返回值

dp[m][n]

b、代码

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid)
    {
        int m = obstacleGrid.size();//有多少行
        int n = obstacleGrid[0].size();//有多少列

        //创建dp表
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));

        //初始化
        dp[1][0] = 1;

        //填表
        for (int i = 1; i <= m; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                if (obstacleGrid[i - 1][j - 1] == 1)
                {
                    dp[i][j] = 0;
                }
                else
                {
                    dp[i][j] = dp[i][j - 1] + dp[i-1][j];
                }
            }
        }

        //返回
        return dp[m][n];

    }
};

 Leetcode 测试结果:   

二、礼物的最⼤价值(medium)

现有一个记作二维矩阵 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) {

    }
};

a、解题思路 

这种路径题目极大可能用动态规划求解

1、转态表示

首先我们想以i,j位置为结尾表示什么

dp[i][j表示:以i,j位置结尾的时候,此时礼物的最大值

 2、状态转移方程

根据最近的一个位置划分:

从[i-1][j]------>[i][j]:

那礼物的不就是dp[i-1][j]+frmae[i-1][j-1];(frmae的坐标是映射过的)

从[i][jj-1]------>[i][j]:

那礼物的不就是dp[i][j-1]+frmae[i-1][j-1];

但是我们要求的是拿到礼物的最大值:

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

3、初始化

这里的初始化就非常简单了,对应[1][1]位置我们肯定是要保证他在填表的时候不变的,那么[1][0]和[0][1]位置肯定是0,至于0行,0列后面的,按理来说是要初始化为负无穷的,但因为礼物值>=0,所以初始化为0也可以。(这里就不用我们初始化了,因为vector是有初始化功能的)

4、 填表顺序

从上往下填写每一行,每一行都是从左往又开始填写 

5、返回值

dp[m][n]

b、代码

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

        //创建dp表
        vector<vector<int>> dp(m+1,vector<int>(n+1));

        //初始化,vector已经完成

        //填表
        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];

    }
};

  Leetcode 测试结果:  

三、 下降路径最⼩和(medium)

给你一个 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) {

    }
};

 

a、解题思路 

上面我们刚刚做完最大价值,现在来做最小和,二者思路非常相似,但是我们这里要仔细读题。

1、转态表示

dp[i][j]表示:到达[i,j]位置时,最小的下降路径

 2、状态转移方程

根据最近的一个位置划分:

从[i-1][j-1]------>[i][j]:

那最小的下降路径不就是dp[i-1][j-1]+m[i][j;

从[i][jj-1]------>[i][j]:

那最小的下降路径不就是dp[i-1][j]+m[i][j;

从[i]-1[jj]------>[i][j]:

那最小的下降路径不就是dp[i-1][j+1]+m[i][j;

 

但是我们要求的是最小的下降路径

 状态转移方程:dp[i][jj = min(x,y,z)+m[i][j];

3、初始化

这里的初始化和前面有点不同,所以我希望大家在做这种类型题目的时候,不要无脑初始化,一定要先进行分析。

  • 首先我们肯定是其想是第一行位置的值, 肯定是其本身(最小的下降路径),根据转态转移方程,我们肯定是不能让我们新添加的行影响结果,所以把第一行初始化为0
  • 在选择dp[1][2]进行观测(不一定非要选择这个位置,合适就好),根据转态转移方程,我们肯定是不能让我们新添加的列影响结果,所以要把第一列初始化为正无穷(第一行元素除外)
  • 在选择dp[1][3]进行观察,,根据转态转移方程,我们肯定是不能让我们新添加的列影响结果所以要把最后一列初始化为正无穷(第一行元素除外)

4、 填表顺序

从上往下填写,值到最后一行

5、返回值

返回最后一行的最小值

b、代码

class Solution {
public:
    int get_min(int x,int y,int z)
    {
        int tmp = min(x,y);
        return min(tmp,z);
    }
    int minFallingPathSum(vector<vector<int>>& matrix)
    {
        int m = matrix.size();
        int n = matrix[0].size();

        int max = INT_MAX;//相当与正无穷
        //创建dp表
        vector<vector<int>> dp(m+1,vector<int>(n+2,max));

        //初始化 这里初始化第一行就好
        for(int i = 0;i<n+2;i++)
        {
            dp[0][i] = 0;
        }

        //填表
        for(int i = 1;i<=m;i++)
        {
            for(int j =1;j<=n;j++)
            {
                dp[i][j] = get_min(dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1])+matrix[i-1][j-1];
            }
        }
        //遍历最后一行找出最小值返回
        int min = max;
        for(int i = 1;i<=n;i++)
        {
            if(dp[m][i]<min) min = dp[m][i];

        }
        return min;
    }
};

  Leetcode 测试结果:   

四、 最⼩路径和(medium)

给定一个包含非负整数的 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) {

    }
};

a、解题思路 

又是这种路径的问题,我相信如果大家,认真做了前面的题,这题思路就非常清晰了

1、转态表示

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

 2、状态转移方程

根据最近的一个位置划分:

从[i-1][j]------>[i][j]:

那最小路径和不就是dp[i-1][j]+g[i][j];

从[i][jj-1]------>[i][j]:

那最小的下降路径不就是dp[i][j-1]+g[i][j];

但是我们要求的是最小的下降路径

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

3、初始化

还先分析第一个位置,那么dp[0][1]和dp[1][0]就要初始化为0,其他位置,我们想现在可以总结一下了:

  • 当求最小值    就初始化为正无尽
  • 当求最大值   就初始化为负无尽

4、 填表顺序

从上往下填写,在从左往右。

5、返回值

dp[m][n]

b、代码

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

        //创建dp
          vector<vector<int>> dp(m+1,vector<int>(n+1,INT_MAX));
        //初始化
        dp[1][0] = 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];
    }
};

  Leetcode 测试结果:    

 

五、地下城游戏(hard) 

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

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

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

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

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

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

示例 1:

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

示例 2:

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

 

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {

    }
};

a、解题思路 

大家遇到这种文字多的题目,不要害怕,如果读不明白,就带这例题求推就好了。

1、转态表示

大家可能非常想到是以某一个位置结尾推出: 

dp[i][j]表示:到达[i,j]位置时,所需的最低血量点数。但这是不正确的,因为我们不仅仅在[i][j]位置受到前面位置的影响,其实我们还会受到后面位置的影响,所以这样的转态表示是不可取的。

但是我们还可以想 一下以某位置为起点来推

dp[i][j]表示从[i,j]位置重出发,到达终点所要的最低健康血量

 2、状态转移方程

根据最近的一个位置划分:

往右走那最低血量应该是:dp[i][j+1] -d[i][j]

往下走那最低血量应该是:dp[i]+1[j] -d[i][j]

但是我们还要注意当d[i][j]此时非常大的时候,dp[i][j]可能出现负值,但是这样是不符合常理的,所以我们要处理一下:dp[i][j] = max(1,dp[i][j]);

,到达终点所要的最低健康血量

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

3、初始化

这里我们填表因为是从后往前面填的,所以我们要关注最后一个位置,当我们救出公主的时候,肯定要保证自己的血量不为0,所以在[m][n-1]和[m-1][n]位置填1就好,其他位置为不影响 状态转移方程应该都初始化为正无穷。

4、 填表顺序

从下往上填写,在从右往左。

5、返回值

dp[0][0]

b、代码

class Solution
{
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon)
    {
        int m = dungeon.size(), n = dungeon[0].size();
        // 建表 + 初始化
        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];
                dp[i][j] = max(1, dp[i][j]);
            }
        // 返回结果
        return dp[0][0];
    }
};

   Leetcode 测试结果:  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1494499.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Stable Diffusion WebUI API http://127.0.0.1:7860/docs空白

在尝试调用Stable Diffusion WebUI API的时候&#xff0c;打开http://127.0.0.1:7860/docs遇到了以下页面 网络诊断是这样的原因&#xff1a; 修bug&#xff0c;改来改去遇到了以下两种页面&#xff1a; 此时http://127.0.0.1:7860可以如下正常显示&#xff1a; 查资料的时候找…

小白跟做江科大51单片机之DS1302按键可调时钟

1.引入上一个程序的代码 2.引入Key和Timer0文件 3.获取按键值 定义全局变量unsigned char keynum main函数中 keynumKey(); 4.设置第一个按键的两种模式&#xff0c;以此来控制时钟的设定和显示 if(keynum1) { if(MODE0) { …

机器视觉相关网站推荐

机器视觉相关网站推荐 - 知乎计算机视觉&#xff08;Computer Vision&#xff09;最近这几年发展迅速&#xff0c;技术论坛和QQ群也如雨后春笋&#xff0c;特别是人脸检测/识别领域。近段时间云从君从研究院那里探得了好几个论坛&#xff0c;收藏了若干网站&#xff0c;记录下&…

numpy——基础知识(创建/类型/形状/运算)(python)

简介 NumPy 是一个 Python 包。它代表 Numeric Python。 它是一个由多维数组对象和用于处理数组的例程集合组成的库。 Numeric&#xff0c;即 NumPy 的前身&#xff0c;是由 Jim Hugunin 开发的。 也开发了另一个包 Numarray &#xff0c;它拥有一些额外的功能。 2005年&#…

《程序员的职业迷宫:选择你的职业赛道》

程序员如何选择职业赛道&#xff1f; 大家好&#xff0c;我是小明&#xff0c;一名在编程迷宫中探索的程序员。作为这个庞大迷宫的探险者&#xff0c;我深知选择适合自己的职业赛道有多么重要。今天&#xff0c;我将分享一些关于如何选择职业赛道的心得&#xff0c;希望能够帮…

贪心 Leetcode 968 监控二叉树

监控二叉树 Leetcode 968 学习记录自代码随想录 给定一个二叉树&#xff0c;我们在树的节点上安装摄像头。 节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。 计算监控树的所有节点所需的最小摄像头数量。 要点&#xff1a;1.想到优先覆盖叶子节点&#xff0c…

【Windows 常用工具系列 14 -- windows 网络驱动映射】

文章目录 windows 网络驱动映射 windows 网络驱动映射 映射网络驱动器的意思是将局域网中的某个目录映射成本地驱动器号。 在windows上将服务器目录映射到本地盘&#xff1a; 进入到服务器执行下面命令既可以看到对应的 IP地址&#xff1a; 将对应的IP地址填入上图中。 映…

【three.js】22. Imported Models导入模型

22. Imported Models导入模型 介绍 Three.js 可以让你创建很多原始几何体&#xff0c;但是当涉及到更复杂的形状时&#xff0c;我们最好使用专用的 3D 软件建模。 在本课中&#xff0c;我们将使用已经制作好的模型&#xff0c;但我们将在以后的课程中学习如何完全在 3D 软件中…

人人都写过的6个bug

大家好&#xff0c;我是知微。 程序员写bug几乎是家常便饭&#xff0c;也是我们每个人成长过程中难以避免的一部分。 为了缓解这份“尴尬”&#xff0c;今天想和大家分享一些曾经都会遇到过的bug&#xff0c;让我们一起来看看这些“经典之作”。 1、数组越界 #include <…

如何给Vue项目配置好一个nginx.conf文件?

如何给Vue项目配置好一个nginx.conf文件&#xff1f; 一般前端项目中&#xff0c;会有一个docker/nginx/nginx.conf文件&#xff0c;用于配置DockerFile配置等。 那么&#xff0c;如何给项目写好一个nginx.conf文件&#xff0c;以DockerFile为例&#xff1a; # 使用 Node.js …

SpringBoot+Mybatis-plus+shardingsphere实现分库分表

SpringBootMybatis-plusshardingsphere实现分库分表 文章目录 SpringBootMybatis-plusshardingsphere实现分库分表介绍引入依赖yaml配置DDL准备数据库ds0数据库ds1 entitycotrollerserviceMapper启动类测试添加修改查询删除 总结 介绍 实现亿级数据量分库分表的项目是一个挑战…

第三百八十五回

文章目录 1.概念介绍2.使用方法3.示例代码 我们在上一章回中介绍了Snackbar Widget相关的内容,本章回中将介绍TimePickerDialog Widget.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1.概念介绍 我们在这里说的TimePickerDialog是一种弹出窗口&#xff0c;只不过窗口的内容…

Windows10 安装Neo4j流程

1、下载并安装ava运行环境 官网链接&#xff08;需要注册Oracle账号&#xff09;&#xff1a;https://www.oracle.com/java/technologies/downloads/ 根据自己Neo4j版本确认需要的JDK版本 百度网盘链接&#xff1a; 链接&#xff1a;链接&#xff1a;https://pan.baidu.com/s/…

Unity 整体界面淡入淡出效果

在Unity中&#xff0c;如果我们要实现控制多个组件同时淡出&#xff0c;同时淡入的效果&#xff0c;可以使用DOTween插件实现。 如图&#xff0c;一个页面中带有背景&#xff0c;一张图片&#xff0c;一个文本&#xff0c;一个滑动条。 要实现以上界面的整体淡入淡出&#xff…

企业内部培训考试系统在线考试都用到了哪些防作弊技术?

企业内部培训考试系统在线考试功能采用了多种技术手段来防止作弊行为&#xff0c;确保考试的公平性和有效性&#xff0c;具体如下&#xff1a; 1. 人脸识别验证&#xff1a;在考试开始前&#xff0c;考生需要进行人脸识别核验。系统会根据考生的姓名和身份证号实时采集人脸与公…

Python 弱引用全解析:深入探讨对象引用机制!

目录 前言 弱引用的概述 弱引用的原理 使用 WeakRef 类创建弱引用 使用 WeakValueDictionary 类创建弱引用字典 实际应用场景 1. 解决循环引用问题 2. 对象缓存 总结 前言 在Python编程中&#xff0c;弱引用&#xff08;Weak Reference&#xff09;是一种特殊的引用方式…

Android开发技巧,最详细的解释小白也能听懂

今天&#xff0c;跟大家聊聊&#xff0c;Framework开发的那些事。 系统应用开发&#xff0c;现在来说&#xff0c;已经开始脱离系统&#xff0c;单独拿出来开发&#xff0c;系统定制接口&#xff0c;已提供给应用调用&#xff0c;用来增强功能。 原生的桌面&#xff0c;拨号&…

【Java面试/24春招】技术面试题的准备

Spring MVC的原理 Mybatis的多级缓存机制 线程池的大小和工作原理 上述问题&#xff0c;我们称为静态的问题&#xff0c;具有标准的答案&#xff0c;而且这个答案不会变化&#xff01; 如果没有Spring&#xff0c;会怎么样&#xff1f;IOC这个思想是解决什么问题&#xff1f…

2024年腾讯云发红包了,可用于抵扣订单金额,你们领了吗?

在2024年腾讯云新春采购节优惠活动上&#xff0c;可以领取新年惊喜红包&#xff0c;打开活动链接 https://curl.qcloud.com/oRMoSucP 会自动弹出红包领取窗口&#xff0c;如下图&#xff1a; 腾讯云2024新春采购节红包领取 如上图所示&#xff0c;点击“领”红包&#xff0c;每…

Android学习笔记在互联网上火了,Android资深架构师分享学习经验及总结

本篇将由 环境搭建、实现原理、编程开发、插件开发、编译运行、性能稳定、发展未来 等七个方面&#xff0c;对当前的 React Native 和 Flutter 进行全面的分析对比&#xff0c;希望能给你更有价值的参考。 前言 移动端跨平台在经历数年沉浮之后&#xff0c;如今还能在舞台聚光…