动态规划之路径问题

news2024/11/19 3:18:13

路径问题

  • 1. 不同路径(medium)
  • 2. 不同路径II(medium)
  • 3. 礼物最大值(medium)
  • 4. 下降路径最小和(medium)
  • 5. 最⼩路径和(medium)
  • 6. 地下城游戏(hard)

1. 不同路径(medium)

1.题目链接:不同路径
2.题目描述:
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
在这里插入图片描述

3.问题分析:
对于动态规划关于路径问题的分析,首先需要选取某个位置,然后以该位置为起点或者结尾进行分析。如这题dp数组中i位置的结果是需要i-1、i-2等位置算出来的,这种情况就以i位置为结尾进行分析。

  1. 状态表示:对于m*n的网格来说,每一个格子可以对应到一个二维数组中的元素,对于从起点到某点(i,j)由多少种不同路径是从起始位置进行计算,所以以(i,j)位置为结尾进行分析。用dp[i][j]表示:从起点位置到[i,j]位置处,一共有多少种方式。有时候是不知道状态表示是正确还是错误的,为此只能先往下分析
  2. 状态转移方程:对(i,j)位置进行分析,dp[i][j]表示从起点位置到[i , j]位置处,一共有多少种方式,那么怎样才能到达 [i , j] 这个位置?因为只能往下或者往右走,所以到达 [i , j] 位置可以是 [i - 1, j] 或者是 [i , j - 1]这两个位置,两个位置的值进行相加 ,就是到达[i , j]位置处总共的方式。因此状态转移方程为:dp[i , j] = dp[i - 1, j] + [i , j - 1]。
  3. 初始化:对于第一行第一列来说,进行-1操作会放生越界情况。可以用if语句判断一下,也可以添加一行一列,多出来的一行一列成为辅助节点,使⽤这种技巧要注意两个点:辅助结点⾥⾯的值要保证后续填表是正确的;下标的映射关系。初始化只将dp[0][1]位置初始化为1即可。
  4. 填表顺序:从上往下填每⼀⾏,在填写每⼀⾏的时候从左往右。
  5. 返回值:返回dp[m][n]。
    在这里插入图片描述

4.代码如下:

class Solution
{
public:
    int uniquePaths(int m, int n)
    {
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0)); // 创建⼀个 dp表
        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];
    }
};

2. 不同路径II(medium)

1.题目链接:不同路径II
2.题目描述
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

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

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
在这里插入图片描述

3.问题分析
这道题和不同路径I区别就是这道题中的网格有障碍物,所以大致分析思路是相同的,就比如这道题是以[i, j]位置为结尾进行分析,状态表示也相同。

  1. 状态表示:dp[i][j] 表示:⾛到 [i, j] 位置处,⼀共有多少种⽅式。
    以[i, j]位置为结尾进行分析,同上到题,到达 [i , j] 位置可以是 [i - 1, j] 或者是 [i , j - 1]这两个位置,两个位置的值进行相加 ,就是到达[i , j]位置处总共的方式;但是如果有障碍物呢?比如[i - 1, j] 或者是 [i , j - 1]其中有一个是障碍物,那么到达[i , j] 位置的所有方法就是其中一个某个不是障碍物的路径数。所以另一个有障碍的路径数为零,即遇到障碍物就不需要计算这个位置上的值,直接让它等于0。
  2. 状态转移方程:同上一题dp[i , j] = dp[i - 1, j] + [i , j - 1]。不过如果这个位置上有障碍物,那么就不需要计算这个位置上的值,直接让它等于 0 即可。
  3. 初始化:同上一题,可以添加⼀行,并且添加⼀列后,只需将dp[1][0]的位置初始化为1 即可。
  4. 填表顺序:从上往下填每⼀⾏,在填写每⼀⾏的时候从左往右。
  5. 返回值:返回dp[m][n]。

在这里插入图片描述4.代码如下

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, 0));
        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)
                {
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }
            }
        }
        return dp[m][n];
    }
};

3. 礼物最大值(medium)

1.题目链接:礼物最大值
2.题目描述
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
在这里插入图片描述

3.问题分析
这道题和上述两道又有所不同,但都属于同种类型,所以方法大致一样。先进行状态表示:用dp[i][j] 表示:⾛到 [i, j] 位置处,此时的最⼤价值。上述两题是要算出路径数,所以将[i - 1, j] 的路径数与[i , j - 1]的路径数相加。而这道题是要求路径上的最大价值,可以将[i - 1, j] 路径上的价值数与[i , j - 1]路径上的价值数进行比较,保留两个中较大的值然后再加上grid[i,j]位置上的值即可。

  1. dp[i][j] 表⽰:⾛到 [i, j] 位置处,此时的最⼤价值
  2. 状态转移方程:从[i - 1, j] 位置,向下⾛⼀步,此时到达 [i, j] 位置能拿到的礼物价值为 dp[i - 1][j] + grid[i][j] ; 从 [i, j - 1] 位置,向右⾛⼀步,此时到达 [i, j] 位置能拿到的礼物价值为dp[i][j - 1] + grid[i][j]。
  3. 初始化:同上一题,可以添加⼀行,并且添加⼀列后,所有的值都为 0 即可。
  4. 填表顺序:从上往下填每⼀⾏,在填写每⼀⾏的时候从左往右。
  5. 返回值:返回dp[m][n]。

在这里插入图片描述

4.代码如下

class Solution
{
public:
    int maxValue(vector<vector<int>>& grid)
    {
        int m = grid.size(), n = grid[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]) + grid[i - 1][j - 1];

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

4. 下降路径最小和(medium)

1.题目链接:下降路径最小和
2.题目描述
给你一个 n x n 的 方形 整数数组 matrix ,请你找出并返回通过 matrix 的下降路径 的 最小和 。
下降路径 可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列(即位于正下方或者沿对角线向左或者向右的第一个元素)。具体来说,位置 (row, col) 的下一个元素应当是 (row + 1, col - 1)、(row + 1, col) 或者 (row + 1, col + 1) 。
在这里插入图片描述
3.问题分析
大致思路不变,先表示状态,用二维数组dp[i][j] 表⽰:到达 [i, j] 位置时,所有下降路径中的最⼩和。然后进行分析,上述所示的问题,因只能向左或向下移动,所以[i,j]的路径数是将[i - 1, j] 的路径数与[i , j - 1]的路径数相加;而这道题位置 (row, col) 的下一个元素应当是 (row + 1, col - 1)、(row + 1, col) 或者 (row + 1, col + 1) ;以[i,j]为结尾进行分析,到达[i,j]位置的上一层应当是[i - 1, j - 1],[i - 1, j],[i - 1, j + 1]中三者最小的一个,然后加上matrix[i,j]的值,然后遍历依次填写dp表即可。其中有越界问题,如[0, j]或[i, n - 1]位置,可以用辅助结点来解决,增加两列,一列在起始,另一列在末尾;最后寻找最后一行最小的值即为下降路径最小和。

  1. dp[i][j] 表⽰:到达 [i, j] 位置时,所有下降路径中的最⼩和。
  2. dp[i][j] = min(dp[i - 1][j], min(dp[i - 1][j - 1], dp[i - 1][j +1])) + matrix[i][j] 。
  3. 初始化:需要加上两列,所有的位置都初始化为⽆穷⼤,将dp表第一行初始为matrix第一行(左右两列的辅助结点是无穷大)
  4. 从上往下填每⼀⾏,在填写每⼀⾏的时候从左往右。
    4.代码如下
class Solution 
{
public:
    int minFallingPathSum(vector<vector<int>>& matrix) 
    {
        int n = matrix.size();
        //初始化dp表,全部初始化为无穷大
        vector<vector<int>> dp(n, vector<int>(n + 2, INT_MAX));
        //=将dp表第一行初始为matrix第一行
        for (int j = 0; j < n; ++j)
            dp[0][j + 1] = matrix[0][j];
        //从第二行第二列开始遍历
        for (int i = 1; i < n; ++i)
        {
            //在dp表中不是辅助结点的位置范围是1到n
            for (int j = 1; j <= n; ++j)
            {
                int x = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i - 1][j + 1]));
                dp[i][j] = x + matrix[i][j - 1]; //没有添加行,matrix对应的映射为i,j-1
            }
        }
        //寻找最后一行最小值
        int ret = dp[n - 1][0];
        for (int j = 1; j <= n; ++j)
            ret = min(ret, dp[n - 1][j]);
        return ret;
    }
};

5. 最⼩路径和(medium)

1.题目链接:最⼩路径和
2.题目描述
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
在这里插入图片描述

3.问题分析
这道题与上述题基本一致了,分析就略过。

  1. 状态表⽰:dp[i][j] 表⽰:到达 [i, j] 位置处,最⼩路径和是多少。
  2. 这道题是要求路径上的最小和,可以将[i - 1, j] 路径上的最小和与[i , j - 1]路径上的最小和进行比较,保留两个中较小的值然后再加上grid[i,j]位置上的值即可。
  3. 添加⼀⾏,并且添加⼀列后,所有位置的值可以初始化为⽆穷⼤,然后让dp[0][1] = dp[1][0] = 0 即可。
  4. 要返回的结果是dp[m][n]。因为添加了一行一列。
    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));
        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];
        return dp[m][n];
    }
};

6. 地下城游戏(hard)

1.题目链接:地下城游戏
2.题目描述
恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。
骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。
有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。
为了尽快解救公主,骑士决定每次只 向右 或 向下 移动一步。
返回确保骑士能够拯救到公主所需的最低初始健康点数。
注意:任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。
在这里插入图片描述

3.问题分析
这道题已知骑士到达公主所在地,也就是右下角,骑士此时所剩健康点数为1 ,求到达终点时,起始位置生命健康值的最小点数。所以这道题应该从右下角开始遍历,至于怎么遍历,看如下分析:状态表示:dp[i][j] 表⽰:从 [i, j] 位置出发,到达终点时所需的最低初始健康点数; 以[i,j]位置为起点进行分析,有很多条路径可以到达终点,路径上有加血的,有减血的,我们要求什么?求的是血最多的时候?不是,求的是应该血量刚够过该路径的值 也就是dungeon数组中路径求和过程中各个路径负数的最大值的绝对值,所以减去 dungeon[i][j]就是所需的生命值,如果为dp结果负数,就说明能量值很大,而dp[i][j] 表⽰:从 [i, j] 位置出发,到达终点时所需的最低初始健康点数,dp[i][j]大于0,出现负数就要赋值为1。负数就代表起始位置生命值最低值为负数,这很显然不肯能为负数
位置[i + 1, j]或者 [i, j + 1]可以到达[i, j] 位置,dp的位置表示最低点数,所以选取 [i + 1, j], [i, j + 1]两者中的较小值减去dungeon[i][j]就是dp[i][j]的最小值。

  1. dp[i][j] 表⽰:从 [i, j] 位置出发,到达终点时所需的最低初始健康点数。
  2. 状态转移方程:dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j];如果dp[i][j] <= 0,则dp[i][j] = 1。
  3. 初始化:在本题中,在 dp 表最后⾯添加⼀⾏,并且添加⼀列后,所有的值都先初始化为⽆穷⼤,然后让dp[m][n - 1] = dp[m - 1][n] = 1 即可。
  4. 填表顺序:需要从下往上填每⼀⾏,每⼀⾏从右往左。
  5. 返回值:dp[0][0]。
    4.代码如下
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];
                if (dp[i][j] <= 0)
                {
                    dp[i][j] = 1;
                }
            }
        }
        // 返回结果
        return dp[0][0];
    }
};

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

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

相关文章

Python Opencv实践 - 图像直方图均衡化

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR) print(img.shape)#图像直方图计算 #cv.calcHist(images, channels, mask, histSize, ranges, hist, accumulate) #images&…

Linux网络编程:多路I/O转接服务器(select poll epoll)

文章目录&#xff1a; 一&#xff1a;select 1.基础API select函数 思路分析 select优缺点 2.server.c 3.client.c 二&#xff1a;poll 1.基础API poll函数 poll优缺点 read函数返回值 突破1024 文件描述符限制 2.server.c 3.client.c 三&#xff1a;epoll …

光谱成像系统视觉均匀校准积分球光源

数字相机的光谱灵敏度是成像传感器、光学透镜、滤光片以及相机内部图像处理过程等诸多因素的综合结果。即使是同一台相机&#xff0c;采用不同的光学镜头和不同的滤光片&#xff0c;由于光学系统的结构和光学材料的透过率不同&#xff0c;导致整个成像系统的光谱灵敏度也有所差…

Linux系统安全:NAT(SNAT、DNAT)

目录 一.NAT 二.SNAT 三.DNAT 一.NAT NAT: network address translation&#xff0c;支持PREROUTING&#xff0c;INPUT&#xff0c;OUTPUT&#xff0c;POSTROUTING四个链 请求报文&#xff1a;修改源/目标IP&#xff0c; 响应报文&#xff1a;修改源/目标IP&#xff0c;根据…

Maven 一键部署到 SSH 服务器

简介 利用 Maven Mojo 功能一键部署 jar 包或 war 包到远程服务器上。 配置 在 maven 的setting.xml 配置服务器 SSH 账号密码。虽然可以在工程的 pom.xml 直接配置&#xff0c;但那样不太安全。 <servers><server><id>iq</id><configuration&…

科技资讯|荷兰电动自行车丢失将被拒保,苹果Find My可以减少丢失

荷兰最大的自行车协会荷兰皇家旅游俱乐部宣布&#xff0c;将不再为胖胎电动自行车提供保险&#xff0c;因为这种自行车的被盗风险极高。 随着电动自行车的销量飙升&#xff0c;胖胎也变得更受欢迎。但问题是&#xff0c;胖胎电动自行车也成为了自行车盗窃者的首选目标。ANWB …

优化时间流:区间调度问题的探索与解决

在浩如烟海的信息时代&#xff0c;时间的有效管理成为了一门不可或缺的艺术。无论是生活中的琐事&#xff0c;还是工作中的任务&#xff0c;时间都在无声地流逝&#xff0c;挑战着我们的智慧。正如时间在日常生活中具有的宝贵价值一样&#xff0c;在计算机科学领域&#xff0c;…

Java IO流(五)Netty实战[TCP|Http|心跳检测|Websocket]

Netty入门代码示例(基于TCP服务) Server端 package com.bierce.io.netty.simple; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGro…

星际争霸之小霸王之小蜜蜂(五)--为小蜜蜂降速

目录 前言 一、思路 二、调整小蜜蜂的移速 三、限制活动范围 四、继续重构 总结 前言 前面我们已经让小蜜蜂左右移动起来了&#xff0c;而且是连续的左右移动&#xff0c;但是在使用的过程中&#xff0c;因为我使用的是笔记本电脑&#xff0c;所以屏幕比较小&#xff0c;设…

Diffusion Models for Time Series Applications: A Survey

Diffusion Models for Time Series Applications: A Survey (Paper reading) Lequan Lin, The University of Sydney Business School, arXiv2023,Cited:5, Code, Paper 1. 引言 扩散模型是一类基于深度学习的生成模型&#xff0c;在前沿的机器学习研究中变得越来越突出。在…

Linux线程 --- 生产者消费者模型(C语言)

在学习完线程相关的概念之后&#xff0c;本节来认识一下Linux多线程相关的一个重要模型----“ 生产者消费者模型” 本文参考&#xff1a; Linux多线程生产者与消费者_红娃子的博客-CSDN博客 Linux多线程——生产者消费者模型_linux多线程生产者与消费者_两片空白的博客-CSDN博客…

测试平台metersphere

metersphere可以做接口测试、UI测试、性能测试。 metersphere接口测试底层是jmeter&#xff0c;可以做API管理&#xff0c;快捷调试&#xff0c;接口用例管理&#xff0c;接口自动化场景执行一键选取用例范围&#xff0c;生成测试报告。 会用jmeter&#xff0c;metersphere会…

软年架构复用-架构师之路(十一)

软件架构复用 软件产品线是 一组产业密集型系统&#xff0c;规定用公用的 核心资产集成 开发而来。 机会复用 和 系统复用。 机会复用&#xff1a;临时发现有可服用资产立马复用。 系统复用&#xff1a;开发之前进行规划好哪些需要复用。 复用的三个阶段&#xff1a; 获取到…

高阶数据结构并查集

目录&#xff1a; 并查集的概念代码实现 并查集的概念 将n个不同的元素划分成一些不相交的集合。开始时&#xff0c;每个元素自成一个单元元素集合&#xff0c;然后按一定的规律将归于同一组元素的集合合并。在此过程中反复遇到查询某一个元素属于那个集合的运算&#xff0c;这…

储能运行约束的Matlab建模方法

最近一段时间有很多人问我最优潮流计算中储能系统的建模方法。部分朋友的问题我回复了&#xff0c;有些没有回消息的&#xff0c;我就不再一一回复了&#xff0c;在这里我写一篇博客统一介绍一下。 1.储能系统介绍 首先&#xff0c;让【GPT】简单介绍一下储能系统&#xff1a;…

【多天线传输技术】BPSK调制信号在AWGN信道下的理论误码率与仿真误码率

%% [0、预处理] clc; clear; close all&#xff1b;%% [1、配置参数] N1000000; %数据点数&#xff08;个&#xff09; SNR_dB0:10; %信噪比&#xff08;dB形式&#xff09; SNR10.^(SNR_dB/10); %信噪比&#xff08;一般形式&#xff0c;Eb/N0&#xff09;…

【业务功能篇78】微服务-前端后端校验- 统一异常处理-JSR-303-validation注解

5. 前端校验 我们在前端提交的表单数据&#xff0c;我们也是需要对提交的数据做相关的校验的 Form 组件提供了表单验证的功能&#xff0c;只需要通过 rules 属性传入约定的验证规则&#xff0c;并将 Form-Item 的 prop 属性设置为需校验的字段名即可 校验的页面效果 前端数据…

Android相机-HAL子系统

引言 应用框架要通过拍照预览摄像获得照片或者视频,就需要向相机子系统发出请求, 一个请求对应一组结果 一次可发起多个请求&#xff0c;并且提交请求是非阻塞的&#xff0c;始终按照接收的顺序以队列的形式先进先出地进行顺序处理 一个请求包含了拍摄和拍照配置的所有信息&…

企业数字化转型中,VR数字展厅能有哪些体验?

在数字化转型的浪潮下&#xff0c;企业纷纷开始注重数字展厅的开展&#xff0c;VR虚拟展厅结合VR全景技术&#xff0c;可以创造出许多有趣的玩法和体验&#xff0c;无论是虚拟参观、互动体验还是VR云会议对接&#xff0c;都为企业客户带来了全新的感知方式。 同传统展厅相比&am…

【LeetCode-中等题】560. 和为 K 的子数组

题目 题解一&#xff1a;逆序枚举数组 //方法一:枚举数组&#xff08;顺序&#xff09;int count 0;// 记录最终符合条件的数组个数int n nums.length;for(int end 0; end<n ; end){int sum 0;//记录每一次经过的元素总和for(int start end; start>0;start--){sum n…