算法【Java】—— 动态规划之路径问题

前言

本文章终点解析第一道题目【不同路径】和最后一道题目【地下城游戏】的动态规划思路,中间几道题目会很快过完,大家如果不熟悉动态规划的思路可以重点看一下这两道题目的解析。

不同路径

https://leetcode.cn/problems/unique-paths

在这里插入图片描述


解析:
首先确定状态表示:根据经验和题目要求,我们将 dp[i][j] 表示为达到 i j 位置一共有多少条路径。

接着推导状态转移方程:首先要到达 i, j 位置一共有两种方式,要么从 i-1,j 向下达到,要么从 i, j-1 向右达到。
我们将达到 i-1, j 和 达到 i , j-1 一共有多少条路径进行相加就可以得到 到达 i, j 位置 一共有多少种方式了。那么如何获得 达到 i-1, j 和 达到 i , j-1 一共有多少条路径?这不就是我们的状态表示吗,即 dp[i-1][j] 和 dp[i][j-1]
所以状态转移方程为 dp[i][j] = dp[i-1][j] + dp[i][j-1]
在这里插入图片描述

现在来进行初始化,为了方便我们填表不发生越界,我们决定多开一列和一行:
在这里插入图片描述
蓝色区域是我们要多开的空间,为什么开的是左边和上面的空间,因为我们的状态转移方程要用到上面和做左边的状态数值,为了避免在求原数组第一行和第一列发生越界,我们就多开了这部分空间,如果你不开,你就要在填表之前把第一行和第一列给提前填好。

初始化还要注意填表的正确性,因为我们多开的空间是会影响到第一行和第一列的状态数值的,我们必须保证第一行和第一列是正确的状态数值。
在这里插入图片描述
第一个状态数值应该为多少?状态表示是到达某个位置一共有多少条路径,那么达到第一个位置应该是一条路径,所以要确保dp[1][1] = 1 的话,我们只需要 dp[0][1] 或者 dp[1][0] 其中一个等于 1 即可,其他全部初始化为0,就可以确保我们的第一行和第一列的正确性。

接下来是填表顺序:因为我们的状态转移方程是需要上一个和左边一个的状态值,所以我们需要先把左边和上面的状态值填完,也就是说填表顺序应该为 从上到下,从左到右。

最后就是返回值,因为题目要求到达终点一共有多少条路径,我们直接返回 dp[m][n] 即可,也就是 dp 表的最后一个位置。

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

不同路径Ⅱ

https://leetcode.cn/problems/unique-paths-ii

在这里插入图片描述


解析:
状态表示:达到 某一个位置一共有多少条路径

状态转移方程:dp[i][j] = dp[i-1][j] + dp[i][j-1]

初始化:因为要用到左边和上边的状态数值,所以上面多开一行,右边多开一列,然后dp[0][1] 或者 dp[1][0] 其中一个设置为 1 ,保证填表的正确性。

填表顺序:从上往下,从左往右

返回值:dp[m][n]

细节处理:因为我们不能通过障碍物,所以遇到障碍物的状态值应该写 0, 不需要状态转移方程来推到其状态值

class Solution {public int uniquePathsWithObstacles(int[][] ob) {//构建 dp 表int m = ob.length;int n = ob[0].length;int[][] dp = new int[m+1][n+1];//初始化dp[0][1] = 1;//填表for(int i = 1; i <= m; i++) {for(int j = 1; j <= n; j++) {if(ob[i-1][j-1] != 1) {dp[i][j] = dp[i-1][j] + dp[i][j-1];}}}//返回值return dp[m][n];}
}

珠宝的最高价值

https://leetcode.cn/problems/li-wu-de-zui-da-jie-zhi-lcof

在这里插入图片描述


解析:
状态表示:到达某一个位置能获得的珠宝的最大价值

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

初始化,为了填表方便,多开空间。
要保证第一行和第一列的正确性,我们只要保证 dp 表的初始状态值为 0 即可,也就不需要什么额外处理了。
注意下标,因为我们多开了空间,如果要访问原数组必须你的 i, j 应该都减去 1,即 jewelleryValue[i-1][j-1]

填表顺序:从上到下,从左到右

返回值:dp[m][n]

class Solution {public int jewelleryValue(int[][] frame) {//建表int m = frame.length;int n = frame[0].length;int[][] dp = new int[m+1][n+1];//填表for(int i = 1; i <= m; i++) {for(int j = 1; j <= n; j++) {dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]) + frame[i-1][j-1];}}//返回值return dp[m][n];}
}

下降路径最小和

https://leetcode.cn/problems/minimum-falling-path-sum

在这里插入图片描述


解析:
状态表示:到达某个位置的路径最小和设为状态值

状态转移方程:要求出某一个位置的状态值,需要利用上面和对角线的左边与右边的状态值。dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i-1][j-1]) + 该位置在原数组的数值。

初始化,因为我们要利用三个状态值,为了避免越界,所以我们多开三个空间,左边一列,上面一行,右边一列:
在这里插入图片描述
为了确保全表正确,也就是不能让多开的空间影响到我们的状态值,所以最上面一行设置为 0,左边一列和右边一列设置为 Integer.MAX_VALUE

在这里插入图片描述

填表顺序:上到下,左到右

返回值,遍历最后一行获得最小值然后返回即可。

class Solution {public int minFallingPathSum(int[][] matrix) {//建表int n = matrix.length;int[][] dp = new int[n+1][n+2];//初始化for(int i = 0; i <= n; i++) {dp[i][0] = dp[i][n+1] = Integer.MAX_VALUE;}Arrays.fill(dp[0], 0);//填表for(int i = 1; i <= n; i++) {for(int j = 1; j <= n; j++) {dp[i][j] = Math.min(Math.min(dp[i-1][j-1], dp[i-1][j]), dp[i-1][j+1]) + matrix[i-1][j-1];}}//返回值int ret = Integer.MAX_VALUE;for(int i = 1; i <= n; i++) {ret = Math.min(ret, dp[n][i]);}return ret;}
}

最小路径和

https://leetcode.cn/problems/minimum-path-sum

在这里插入图片描述


解析:
状态表示:到达某一个位置的路径最小和

状态转移方程:dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + 该位置在原数组的数值

初始化:多开上面一行和左边一列的空间,为了保证填表的正确性,多开的空间先全部设置为 Integer.MAX_VALUE,然后将 dp[0][1] 或者 dp[1][0] 设置为 0 即可。

填表顺序:从上到下,从左到右

返回值:dp[m][n]

class Solution {public int minPathSum(int[][] grid) {//建表int m = grid.length;int n = grid[0].length;int[][] dp = new int[m+1][n+1];//初始化for(int i = 0; i <= m; i++) {dp[i][0] = Integer.MAX_VALUE;}Arrays.fill(dp[0], Integer.MAX_VALUE);dp[0][1] = 0;//填表for(int i = 1; i <= m; i++) {for(int j = 1; j <= n; j++) {dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + grid[i-1][j-1];}}//返回值return dp[m][n];}
}

地下城游戏

https://leetcode.cn/problems/dungeon-game

在这里插入图片描述


解析:
状态表示:到达某一个位置时所需要的最低血量,这个状态表示是不能推导出状态转移方程的,假设你硬推,大概率是推出 dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + 原数组对应的数值。可是这个方程是错误的,因为你到了 i, j 这个位置得到的最低血量可能不能支撑后面的前进,所以你的状态转移方程还需要考虑后面的情况,可是你考虑不到了,因为后面的状态值根本就还没填你是无法获取的,因此这个状态表示是不可行的。

那么我们就要另辟蹊径了,状态表示除了以某个位置结尾还可以以某个位置为起点,那么我们现在使用以某个位置为起点的形式来看看能不能推导出来。

那么新的状态表示应该为 从某一个位置出发所需要的最低血量。

状态转移方程:要想获得 dp[i][j] 就要知道右边和下面所需要的最低血量,求出其中的最小值,而右边和下面所需要的最低血量正好对应我们的状态表示,即右边的最低血量为 dp[i][j+1] ,下面的最低血量为 dp[i+1][j],那么状态转移方程为 dp[i][j] = min(dp[i+1][j],dp[i][j+1]) - 原数组对应的数值。这方程是这样推导的:首先该位置的最低血量 + 原数组对应的数值 要等于到往下一个位置的 最低血量,然后移项就可以得到上面的状态转移方程

这里还有一个细节:如果原数组是一个血包,也就是一个正数,是有可能让我们的 状态值推导为 小于等于 0 的,这个状态值骑士都死了,还救什么公主,所以这种情况我们要判断如果是则直接设置为 1。

初始化:因为我们的状态推导需要用到右边和下边的状态值,所以为了避免越界和方便填表,我们下面多开一行,右边多开一列。然后就是要保证填表的正确性,也就是保证我们右边和下面的状态值不因为多开的空间而发生错误。首先先来考虑右下角:
在这里插入图片描述
首先要明确绿色的框框是我们救出公主后达到这里多需要的最低血量,应该为 1, 因为骑士的血量不能为0也不能为负值否则就直接死亡了,死了根本就道理绿色区域。

剩余蓝色的区域我们应该设置为 Integer.MAX_VALUE,避免蓝色区域影响到我们的填表正确性。

填表顺序:根据状态转移方程,我们需要先知道右边和下面的状态值才能进行推导,所以从下到上,从右到左进行填表。

返回值:从左上角到右下角所需要的最低血量,返回 dp[0][0] 即可。

class Solution {public int calculateMinimumHP(int[][] dungeon) {//建表int m = dungeon.length;int n = dungeon[0].length;int[][] dp = new int[m+1][n+1];//初始化for(int i = 0; i <= m; i++) {dp[i][n] = Integer.MAX_VALUE;}Arrays.fill(dp[m], Integer.MAX_VALUE);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] = Math.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.mzph.cn/news/885417.shtml

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

相关文章

FPGA实现串口升级及MultiBoot(五)通过约束脚本添加IPROG实例

本文目录索引 一个指令和三种方式通过约束脚本添加Golden位流工程MultiBoot位流工程验证example1总结代码缩略词索引: K7:Kintex 7V7:Vertex 7A7:Artix 7MB:MicroBlaze上一篇文章种总结了MultiBoot 关键技术,分为:一个指令、二种位流、三种方式、四样错误。针对以上四句话我…

jmeter基础02_下载安装jmeter

&#xff08;安装包windows、mac、Linux通用&#xff09; Step1. 官网下载 官网地址&#xff1a;https://jmeter.apache.org/download_jmeter.cgi 官网可见最新版本的jmeter和要求的jdk版本&#xff0c;先说结论&#xff1a;建议下载Binaries-zip格式包即可。 安装包有2大类&am…

Spring Boot环境下的知识分类与检索

2 开发技术 2.1 VUE框架 Vue.js&#xff08;读音 /vjuː/, 类似于 view&#xff09; 是一套构建用户界面的渐进式框架。 Vue 只关注视图层&#xff0c; 采用自底向上增量开发的设计。 Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。 2.2 Mysql数据库 …

【Threejs】相机控制器动画

使用场景 官方提供了一个基于目标点、刷新速度&#xff0c;在每次renderer中执行的动画&#xff0c;但实际开发中你可能会需要基于设定时间、目标点添加动画&#xff0c;并且有更多自定义成分的方式 获取当前状态下控制器和相机的姿态 getVisionCof() {let { controls } thi…

理解鸿蒙app 开发中的 context

是什么 Context是应用中对象的上下文&#xff0c;其提供了应用的一些基础信息&#xff0c;例如resourceManager&#xff08;资源管理&#xff09;、applicationInfo&#xff08;当前应用信息&#xff09;、dir&#xff08;应用文件路径&#xff09;、area&#xff08;文件分区…

【JS】如何设置一个只读属性

1. Object.defineProperty 通过属性描述符将属性设置为不可修改 const obj {}; Object.defineProperty(obj, name, {value: John,writable: false, // 不允许修改该属性configurable: true, // 允许删除或修改属性描述符 });console.log(obj.name); // 输出: Johnobj.name …

YOLOv6-4.0部分代码阅读笔记-inferer.py

inferer.py yolov6\core\inferer.py 目录 inferer.py 1.所需的库和模块 2.class Inferer: 3.class CalcFPS: 1.所需的库和模块 #!/usr/bin/env python3 # -*- coding:utf-8 -*- # 用于模型的推理。 import os import cv2 import time import math import torch import…

Linux:git的了解和基础使用(保姆级教程)

文章目录 引言一、git是什么1.1 版本控制器git1.2 git的历史 二、git的使用2.1 安装git2.2 创建gitee账号2.3 git三板斧2.3.1 add2.3.2 commit2.3.3 push 三. git的补充总结 引言 git是一款软件&#xff0c;它用于帮助我们来管理代码以及文件&#xff0c;掌握并使用git可以很有…

探索LINQ在C#中的应用:从基本查询到数据联接

LINQ&#xff08;语言集成查询&#xff09;是微软为.NET框架开发的一种强大功能&#xff0c;于2007年作为C# 3.0和Visual Basic .NET 9.0的一部分引入。LINQ的诞生旨在提供一种一致且直观的方式来查询和操作数据&#xff0c;无论数据来源是内存中的集合、数据库还是XML文档。 …

鸿蒙UI开发——实现环形文字

1、背 景 有朋友提问&#xff1a;您好关于鸿蒙UI想咨询一个问题 如果我想实现展示环形文字是需要通过在Text组件中设置transition来实现么&#xff0c;还是需要通过其他方式来实现。 针对这位粉丝朋友的提问&#xff0c;我们做一下解答。 2、实现环形文字效果 ❓ 什么是环形…

搭建轻量级文件服务器Dufs

前言 Dufs是什么&#xff1f; 答&#xff1a;是一款轻量级文件管理服务器&#xff0c;类似于FTP服务器但又比FTP更好用易于管理。 Dufs有什么特性&#xff1f; 答&#xff1a; ‌静态文件服务…

什么时候用 Tailwind 什么时候用 CSS

结合自己开发项目&#xff0c;进行整理 对比图 特性/场景Tailwind CSS普通 CSS常见布局✅ margin, padding, flex, grid⚠️ 可用但繁琐基础样式✅ 颜色、字体、间距等预设值⚠️ 需要手动定义响应式设计✅ sm:, md:, lg: 前缀⚠️ 需要写 media queries特定数值❌ 受限于预设…

【软考】系统分析师第二版 新增章节 第20章微服务系统分析与设计

微服务系统是一类基于微服务架构风格的分布式系统&#xff0c;它将应用程序拆分成多个独立的小型服务&#xff0c;每个服务都运行在独立的进程中&#xff0c;并采用轻量级通信协议进行通信。这些服务可以由不同的团队开发、不同的编程语言编写&#xff0c;并且可以按需部署。微…

JS类型检测大全:从零基础到高级应用

文章目录 1. typeof 操作符typeof null 为什么是 object 2. instanceof 操作符3. Object.prototype.toString.call()&#xff08;最准确的&#xff09;4. Array.isArray()5. constructor 属性总结 在JavaScript中&#xff0c;有多种方法可以用来检测数据类型。每种方法都有其特…

基于SSM的校园美食交流系统【附源码】

基于SSM的校园美食交流系统 效果如下&#xff1a; 管理员主页面 用户主页面 美食信息页面 美食资讯页面 修改密码页面 论坛中心页面 研究背景 随着高校信息化建设的不断推进&#xff0c;校园生活日益丰富多样&#xff0c;学生对于美食的需求与探索也愈发旺盛。然而&#xff…

css实现div被图片撑开

固定好盒子的宽度&#xff0c;高度随传过来的图片大小决定 <div class"tab-con"> <img:src"concertInfo.detail"alt""> </div>.tab-con {margin-bottom: 20px;width: 700px;img {width: 700px;height: auto;object-fit: cont…

PICO+Unity MR空间网格

官方链接&#xff1a;空间网格 | PICO 开发者平台 注意&#xff1a;该功能只能打包成APK在PICO 4 Ultra上真机运行&#xff0c;无法通过串流或PICO developer center在PC上运行。使用之前要开启视频透视。 在 Inspector 窗口中的 PXR_Manager (Script) 面板上&#xff0c;勾选…

斗破QT编程入门系列之前言:认识Qt:获取与安装(四星斗师)

本系列是在学习完C之后&#xff0c;然后通过Qt构建界面来&#xff0c;赋予枯燥的代码新的样貌&#xff0c;这样我们才能开发出更人性化的程序&#xff0c;同时会进一步提高初学者对编程的兴趣&#xff0c;大家加油&#xff0c;斗破Qt来了。 斗破Qt目录&#xff1a; 斗破Qt编程…

PyTorch核心概念:从梯度、计算图到连续性的全面解析(三)

文章目录 Contiguous vs Non-Contiguous TensorTensor and ViewStrides非连续数据结构&#xff1a;Transpose( )在 PyTorch 中检查Contiguous and Non-Contiguous将不连续张量&#xff08;或视图&#xff09;转换为连续张量view() 和 reshape() 之间的区别总结 参考文献 Contig…

家庭宽带如何开启公网ipv4和ipv6

好久没更新了&#xff0c;最近在家里折腾nas。一来自己有下电影的习惯&#xff0c;二来手机的icloud容量也不够了。所以买了群晖的423,但是nas要想用的畅快&#xff0c;外网访问必不可少。所以我之前研究了下&#xff0c;打家里的电信快带打通了外网。 一般nas的服务商基本都会…