60题学会动态规划系列:动态规划算法第二讲

news2024/11/24 5:02:46

都是路径问题~

文章目录

  • 1.不同路径
  • 2.不同路径II
  • 3.礼物的最大价值
  • 4.下降路径最小和
  • 5.最小路径和


1.不同路径

力扣链接:力扣

一个机器人位于一个 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[i-1][j]的时候会越界,第一列的值求dp[i][j-1]的时候会越界,所以我们只需要多开一行一列就能完成初始化:

那么这个多开的虚拟位置如何初始化呢?我们的虚拟位置的值一定不可以影响最终结果,首先第一行不管是哪一个位置他们的方法数都为1,因为这个机器人只能向右或者向下。然后第一列也同理所有的方法数都为1,这个时候我们看第一行最右边的位置,这个位置需要上面位置的方法数和左边位置的方法数之和,首先起点位置是一种方法,所以我们的虚拟位置一定为0,但是为了保证起点为1,所以只需要让起点的上一个位置或者左边的位置为1即可,如下图:

4.填表顺序

整体从上往下,每一行从左往右依次填表

5.返回值

返回dp表最后一个位置

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

 要注意:我们的虚拟位置是不进行动态计算的,所以实际上的位置是从1,1开始的,从上面的图我们也可以发现。刚开始二维数组初始化为0会方便很多。

2.不同路径II

力扣链接:力扣

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

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

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

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

 此题与上一题一样,只不过障碍物的位置方法数为0.

1.状态表示

dp[i][j]表示到达i,j位置的方法数。

2.状态转移方程

当dp[i][j]!=1时,dp[i][j] = dp[i-1][j] + dp[i][j-1]

当dp[i][j]==1时,dp[i][j]==0

3.初始化

与上一题一样

4.填表顺序

与上一题一样

5.返回值

与上一题一样

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
       //创建dp表
       int row = obstacleGrid.size();
       int col = obstacleGrid[0].size();
       vector<vector<int>> dp(row+1,vector<int>(col+1,0));
       //初始化
       dp[1][0] = 1;
       //状态转移方程
       for (int i = 1;i<=row;i++)
       {
           for (int j = 1;j<=col;j++)
           {
               if (obstacleGrid[i-1][j-1]==1)
               {
                   dp[i][j] = 0;
               }
               else 
               {
                   dp[i][j] = dp[i-1][j] + dp[i][j-1];
               }
           }
       }
       return dp[row][col];
    }
};

注意:dp表由于多开了虚拟位置所以与原数组映射整体向右下角移动了一位。

3.礼物的最大价值

力扣链接:力扣

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

 此题实际上还是路径问题,下面我们画个图演示:

其实做了这么多道动态规划题我们也有些经验了,本题的条件还是每次只能向下或者向右移动,那么到达右下角拿到的最大价值的礼物其实就是右下角上面位置和右下角左边位置中拿到的礼物的价值的最大值再加上右下角的礼物价值,下面我们直接按步骤解题:

1.状态表示

dp[i][j]表示到达i,j位置的最大礼物价值

2.状态转移方程

dp[i][j] = max(dp[i-1][j],dp[i][j-1]) + 原数组[i][j]

3.初始化

我们还是用多开虚拟位置的方式初始化,下面我们画图演示:

 题目中有一个小细节:所有礼物的价值都是大于0的,而我们的虚拟位置是不能影响结果的,起点位置的价值就是1不能被影响那么虚拟位置只能是0了,当我们填0后发现所有的结果都是正确的。

4.填表

整体从上往下,每一行从左向右依次填表

5.返回值

返回dp表最后一个位置 

class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
       //创建dp表
       int row = grid.size();
       int col = grid[0].size();
       //初始化
       vector<vector<int>> dp(row+1,vector<int>(col+1,0));
       //状态转移方程
       for (int i = 1;i<=row;i++)
       {
           for (int j = 1;j<=col;j++)
           {
               dp[i][j] = max(dp[i-1][j],dp[i][j-1]) + grid[i-1][j-1];
           }
       }
       return dp[row][col];
    }
};

4.下降路径最小和

力扣链接:力扣

给你一个 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]既然是到达i,j位置的最小下降和,那么状态转移方程一定是看从哪几个位置可以到达i,j位置并且求最小值。

dp[i][j] = min(min(dp[i-1][j-1],dp[i-1][j]),dp[i-1][j+1]) + 原数组[i][j]

3.初始化

我们依次用开虚拟位置的方式,下面我们画个图:

 越界的位置就是我画圈的位置,那么我们如何初始化才不会影响结果呢?这里一定不能将虚拟位置初始化为0,因为题目中是有负数的!:

 所以我们要求最小值不影响原来的位置的值,只能将虚拟位置初始化为无穷大,这样虚拟位置就一定不会是最小值了。如下图:

 首先第一行是不需要计算的,所以第一行的值我们要保持原始结果,那么上面虚拟位置就不能影响我们第一行的值,所以第一行的虚拟位置初始化为0.第一列和最后一列在计算的时候不能影响我们求最小值,所以我们将这两列虚拟位置初始化为正无穷。

4.填表顺序

从上到下,每一行从左向右

5.返回值

我们的状态表示是到达i,j位置的最小和,那么只要找到dp表最后一行的最小值即可

class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& matrix) {
       //创建dp表
       int row = matrix.size();
       int col = matrix[0].size();
       vector<vector<int>> dp(row+1,vector<int>(col+2,INT_MAX));
       //初始化
       for (int i = 0;i<col+2;i++)
       {
           dp[0][i] = 0;
       }
       //状态转移方程
       for (int i = 1;i<=row;i++)
       {
           for (int j = 1;j<=col;j++)
           {
               dp[i][j] = min(min(dp[i-1][j-1],dp[i-1][j]),dp[i-1][j+1])+matrix[i-1][j-1];
           }
       }
       int min = dp[row][0];
       for (int i = 1;i<=col;i++)
       {
           if (dp[row][i]<min)
           {
               min = dp[row][i];
           }
       }
       return min;
    }
};

 我们初始化vector的时候全部初始化为正无穷会比初始化0方便很多,初始化0我们要修改两列的值,初始化正无穷只需要修改第一行的值,将第一行初始化为0,由于我们的min函数只能是两个参数,所以我们先求前两个的最小值再和最后一个值求一下最小值即可,最后我们将dp表最后一行的最小元素拿出来即可。注意:行列的判断条件要画图去看,因为我们实际上是不算虚拟位置的所以一定要控制好判断条件。

5.最小路径和

力扣链接:力扣

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

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

这道题的条件和之前一样,都是只能往下或者往右走,在这种条件下到达右下角,那么右下角的最小值一定是右下角的上面位置或者右下角的左边位置的最小值。

1.状态表示

dp[i][j]表示从起点到达i,j位置的最小值。

2.状态转移方程

能到达i,j位置一定是[i-1][j]和[i][j-1]这两个位置

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

3.初始化

我们继续用开虚拟位置的思想,根据状态转移方程我们知道能越界的位置只有第一行和第一列,所以我们只需要多开一行和多开一列即可:

 我们的起始位置的最小值只能是1,所以起始位置的上面的位置和左边的位置不能影响起始位置的值,所以将1的上面和左边初始化为0,其他位置为了不影响计算最小值所以要初始化为无穷大。

4.填表

从上往下,每行从左向右依次填表

5.返回值

dp表最后一个位置的值。

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
      //创建dp表
      int row = grid.size();
      int col = grid[0].size();
      vector<vector<int>> dp(row+1,vector<int>(col+1,INT_MAX));
      dp[0][1] = dp[1][0] = 0;
      //状态转移方程
      for (int i = 1;i<=row;i++)
      {
          for (int j = 1;j<=col;j++)
          {
              dp[i][j] = min(dp[i-1][j],dp[i][j-1]) + grid[i-1][j-1];
          }
      }
      return dp[row][col];
    }
};

以上就是我们动态规划第二篇的5道题,本次的习题都是路径问题,对于动态规划中的路径问题,我们只要掌握了规律就会非常简单。

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

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

相关文章

异常检测学习笔记 一、异常检测及基本统计

一、什么是异常? 1、异常的定义 异常是指与其他数据有很大不同的数据。异常现象是“一种与其他观测结果大相径庭的观测结果,以至于人们怀疑它是由不同的机制产生的。”,也被称为“异常”或“离经叛道”。 所有观测值 = 正常数据 + 异常值 异常值 = 噪声 + 异常 噪声 = 人们…

scratch接水果 少儿编程 电子学会图形化编程scratch编程等级考试二级真题和答案解析2023年5月

目录 scratch接水果 一、题目要求 1、准备工作 2、功能实现 二、案例分析

操作系统复习2.3.5-管程

引入管程 PV操作困难&#xff0c;容易书写出错&#xff0c;引入管程&#xff0c;作为一种高级同步机制 组成 局限于管程的共享数据结构说明对该数据结构进行操作的一组过程对局部于管程的共享数据结构设置初始值的语句管程有一个名字 基本特征 局限于管程的数据只能被局限…

括号匹配(使用链栈实现)

目录 0. 前言1. 括号匹配——题目描述2. 解题思路3. 括号匹配意义 0. 前言 数据结构——括号匹配(使用链栈实现) 操作系统&#xff1a;Windows10 家庭版 开发环境&#xff1a;Dev-Cpp 1. 括号匹配——题目描述 给定一个只包含括号的字符串s&#xff0c;判断这个字符串中的…

【图】单源最短路径

最短路径 图上的最短路径&#xff1a;两顶点之间经过的边数最少的路径&#xff1b; 网上的最短路径&#xff1a;两顶点之间经过的边上权值之和最少的路径&#xff08;源点->终点&#xff09;。 a星算法、迪杰斯特拉算法、佛洛依德算法。 迪杰斯特拉算法 单源最短路径按…

SpringBoot项目登录并接入MFA二次认证

MFA多因素认证(Multi-Factor Authentication )&#xff1a; 一些需要身份认证的服务&#xff08;如网站&#xff09;&#xff0c;为了提升安全性&#xff0c;通常会在账号密码登录成功后&#xff0c;要求用户进行第二种身份认证&#xff0c;以确保是正确用户登录&#xff0c;避…

【C语言】C预处理器(宏、文件包含、条件编译...)

一、C语言编译的预处理阶段1.1 C语言的编译过程1.2 C语言编译的预处理 二、C语言 宏2.1替换常量2.2函数宏2.3 字符串化和连接&#xff1a;#和##2.4 变参宏 三、文件包含&#xff1a;#include3.1 写法3.2 头文件的作用——声明3.3 头文件和extern 、static 四、 其他指令4.1 #un…

Ansible基础4——变量、机密、事实

文章目录 一、变量二、机密2.1 创建加密文件2.2 查看加密文件2.3 编辑加密文件内容2.4 加密现有文件2.5 解密文件2.6 更改加密密码 三、事实3.1 收集展示事实3.2 展示某个结果3.3 新旧事实命令3.4 关闭事实3.5 魔法变量 一、变量 常设置的变量&#xff1a; 要创建的用户要安装的…

【C++ 基础篇:19】:类的构造函数与初始化列表:用法说明及构造函数的细节内容补充!

本系列 C 相关文章 仅为笔者学习笔记记录&#xff0c;用自己的理解记录学习&#xff01;C 学习系列将分为三个阶段&#xff1a;基础篇、STL 篇、高阶数据结构与算法篇&#xff0c;相关重点内容如下&#xff1a; 基础篇&#xff1a;类与对象&#xff08;涉及C的三大特性等&#…

Kubernetes_容器网络_循序渐进地学习kubernetes网络

文章目录 前言一、Linux网络命名空间1.1 linux网络命名空间1.2 不同网络命名空间的通信两个网络命名空间通信多个网络命名空间通信 二、K8S Pod网络通信2.1 Pod内部容器的网络通信2.2 相同node: 不同pod间的网络通信2.3 不同node: 不同pod间的网络通信2.4 容器网络插件: Flanne…

C++STL库之map

文章目录 关于仿函数stackdeque&#xff08;双端对列&#xff09;queuepriority_queuemap(重点)set(去重) 关于仿函数 //C不能重载的运算符sizeof、 ::、 ? :、 .、 *、 class Add { public:int operator()(int a, int b)const{return a b;} }; //函数对象&#xff0c;仿函数…

EDA数字钟(三)

文章目录 前言一、设计内容二、模块结构三、代码编写1、顶层模块Digclk2、状态控制模块Ctrl3、按键消抖模块Filter4、计时模块Time5、闹钟模块Alarm6、显示模块Display7、数码管驱动模块Smg 四、测试文件五、波形仿真总结 前言 再次编写数字钟Verilog程序&#xff0c;使其符合…

数据迁移工具,用这8种就够了

前言 最近由于工作需要需要进行数据迁移&#xff0c;那么ETL数据迁移工具该用哪些呢&#xff1f; ETL(是Extract-Transform-Load的缩写&#xff0c;即数据抽取、转换、装载的过程)&#xff0c;对于企业应用来说&#xff0c;我们经常会遇到各种数据的处理、转换、迁移的场景。…

50 Projects 50 Days - Split Landing Page 学习记录

项目地址 Split Landing Page 展示效果 Split Landing Page 实现思路 当鼠标移动到左右两块区域时&#xff0c;分别给容器添加不同的class实现样式的变换。 有两种思路可以实现&#xff0c;一种是hover时改变宽度&#xff0c;一种是hover时改变flex拉伸比例&#xff0c;两…

从零手写操作系统之RVOS外设中断实现-04

从零手写操作系统之RVOS外设中断实现-04 RISC-V 中断&#xff08;Interrupt&#xff09;的分类RISC-V Trap &#xff08;中断&#xff09;处理中涉及的寄存器寄存器 mie、mip中断处理流程PLIC 介绍外部中断&#xff08;external interrupt &#xff09;PLICPLIC Interrupt Sour…

精调万分(Fine tune SAM)-万分预测器的解读和精调之一

缘起 分割万物(segment-anything model, SAM, 万分), 是图像分割领域的革命, 图像分割从此进入大模型时代. 如何自定义这个大模型以为己用? 或者说, 通过精调取长补短用于自己的项目?这是一个值得研究的问题, 在这里我试着探索一下, 万分在医学影像学里面的脊柱分割的应用. …

【sentinel】滑动时间窗口算法在Sentinel中的应用

固定窗口算法&#xff08;计数器法&#xff09; 算法介绍 计数器法是限流算法里最简单也是最容易实现的一种算法。比如我们规定&#xff0c;对于A接口来说&#xff0c;我们1秒的访问次数不能超过10次。那么我们可以这么做&#xff1a;在一开始的时候&#xff0c;我们可以设置…

ESP-BOX官方例程实践

1.下载esp-box项目代码 github仓库&#xff1a;https://github.com/espressif/esp-box gitee仓库&#xff1a;https://gitee.com/EspressifSystems/esp-box 使用git工具和如下命令进行下载&#xff1a; git clone --recursive https://github.com/espressif/esp-box.git or gi…

【C++ 基础篇:21】:friend 友元四连问:什么是友元?友元类?友元函数?什么时候用友元?

本系列 C 相关文章 仅为笔者学习笔记记录&#xff0c;用自己的理解记录学习&#xff01;C 学习系列将分为三个阶段&#xff1a;基础篇、STL 篇、高阶数据结构与算法篇&#xff0c;相关重点内容如下&#xff1a; 基础篇&#xff1a;类与对象&#xff08;涉及C的三大特性等&#…

S7-200 PLC的CPU模块介绍

更多关于西门子S7-200PLC内容查看&#xff1a;西门子200系列PLC学习课程大纲(课程筹备中) 1.什么是西门子200PLC的CPU? 如下图1-1所示&#xff0c;S7-200 PLC CUP是将一个微处理器&#xff0c;一个集成电源&#xff0c;一定的数字量或模拟量I/O&#xff0c;一定的通信接口等…