算法王冠上的明珠——动态规划之路径问题(第一篇)

算法王冠上的明珠——动态规划之路径问题(第一篇)

目录

1. 什么叫路径类动态规划

一、核心定义(通俗理解)

二、核心特征(识别这类问题的关键)

2. 动态规划步骤

状态表示

状态转移方程

初始化

填表顺序

返回值

3. 例题讲解

3.1 LeetCode62. 不同路径

3.2 LeetCode63. 不同路径 II

3.3 LeetCodeLCR 166. 珠宝的最高价值


今天我们来聊一聊动态规划的路径类问题。

1. 什么叫路径类动态规划

路径类动态规划是 动态规划的一个重要分支,核心解决 “从起点到终点的路径相关问题”—— 比如 “路径总数”“最短路径长度”“路径上的最大 / 最小和” 等,其本质是通过 “状态递推” 避免重复计算,高效求解多阶段决策的路径问题。

一、核心定义(通俗理解)

把问题想象成 “走迷宫”:

  • 起点:初始状态(如网格的左上角);
  • 终点:目标状态(如网格的右下角);
  • 路径:从起点到终点的每一步选择(如只能向右 / 向下走);
  • 约束条件:每一步的限制(如不能走障碍物、只能走特定方向);
  • 目标:求路径的数量、最短距离、最大收益等。

动态规划的核心是 “记住每一步的结果”:比如走到网格的(i,j)位置时,已有的路径数 / 最短距离,后续计算无需重复推导,直接基于前面的结果递推。

PS:一般来说,路径类的问题不仅可以用动态规划,还可以使用bfs和dfs

二、核心特征(识别这类问题的关键)

  1. 无后效性:走到(i,j)的路径只和 “之前的步骤” 有关,和 “之后的步骤” 无关(比如走到(i,j)有 5 条路径,不管之后怎么去终点,这 5 条路径的数量是固定的);
  2. 重叠子问题:从起点到不同位置的路径会重复经过某些中间状态(比如走到(i,j)可能需要先经过(i-1,j)(i,j-1),这两个状态的路径数需要反复用到);
  3. 最优子结构:如果求 “最短路径”,那么走到(i,j)的最短路径,一定是从(i-1,j)(i,j-1)的最短路径中选更优的那个(子问题的最优解能推出原问题的最优解)

2. 动态规划步骤

这个步骤的话就和前面的斐波那契数列类的动态规划一样。

状态表示

状态表示就是我们数组对应的那个位置的值的含义,简单来说就是那个值代表着什么。比如说我们前面说的前缀和,那他的状态表示就是代表着原数组前面这些数的累加。

状态转移方程

状态转移方程就是根据上面的状态表示来得到的一个公式,比如说我们前面说的前缀和,它的状态转移方程就是dp[i]=dp[i-1]+nums[i]。

初始化

初始化的作用简单来说就是为了防止数组越界访问,所以我们在一开始会给dp表的一小部分值提前给好。方便我们后续计算。拿前缀和来说就是第0个位子我们会直接给0。

填表顺序

之所以我们要有填表顺序,是因为我们填当前位置的值会使用到前面的一些值,那么我们要确保前面的这些值都已经计算好了。

返回值

返回值就是返回题目要求的那个值。

3. 例题讲解

3.1 LeetCode62. 不同路径

我们来看这道题,题目就是要求我们在一个二维数组里面,计算机器人从左上角走到右下角的路径总数。

所以在这道题里面它的状态表示就是走到当前位置的路线数。

所以在这道题里面它的状态转移方程就是dp[i][j]=dp[i-1][j]+dp[i][j-1];

PS:我们在这里需要明白为什么是相加,这是因为题目里面说到达这个位置的方式只有上面和右边,那么我们到达该位置的数量就是这两个位置的相加。

我们在这边需要多设置一行一列,它的初始化就是在把虚拟位置的[0][1]给设置为1.

PS:这边之所以这么设置是因为当前位是需要它上面的和左边的值。如果不这样设置的话,就会发生越界访问。(当然我们也可以不设置,直接把原本的第0行和第0列全部设置为1,这样也是可以的。但是不推荐这样写,因为现在这些题目都是简单题,如果遇到一些难的题目的话,就会理不清了,所以建议还是多设置一行一列)

填表顺序就是一行一行的填写。

返回值就是dp[m][n]。

class Solution { public: int uniquePaths(int m, int n) { vector<vector<int>> dp(m+1,vector<int>(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]; } };

3.2 LeetCode63. 不同路径 II

这道题目的话和上面那一道题目很像,都是在一个二维数组里面,计算机器人从左上角走到右下角的路径总数。唯一的区别就是这道题给的数组里面是存在石头的,也就是需要机器人绕路的点。

所以在这道题里面它的状态表示就是走到当前位置的路线数。

所以在这道题里面它的状态转移方程就是dp[i][j]=dp[i-1][j]+dp[i][j-1],如果遇到石头的话就是0。

我们在这边需要多设置一行一列,它的初始化就是在把虚拟位置的[0][1]给设置为1.

填表顺序就是一行一行的填写。

返回值就是dp[m][n]。

class Solution { public: int uniquePathsWithObstacles(vector<vector<int>>& ob) { int m=ob.size(); int n=ob[0].size(); vector<vector<int>> dp(m+1,vector<int>(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]=0; else dp[i][j]=dp[i-1][j]+dp[i][j-1]; } } return dp[m][n]; } };

3.3 LeetCodeLCR 166. 珠宝的最高价值

这道题的话和前面两道题目不太一样,题目是要求我们到达右下角时整个途中经过的数组数字和最大。

这道题的话是带着一点贪心的思想在里面的,我们在设计代码的时候也是同样的,就是要让dp表里面的每一个位置都是代表走到这个位置时所能达到的最大值。

所以在这道题里面它的状态表示就是走到当前位置时能达到的最大值。

所以在这道题里面它的状态转移方程就是dp[i][j]=max(dp[i-1][j],dp[i][j-1])+f[i-1][j-1]。

(+f[i-1][j-1]是因为还需要加上当前位置的值,也就是走到这个位置时的自身值)

我们在这边需要多设置一行一列,它的初始化就是在把虚拟位置的[0][1]给设置为1.

填表顺序就是一行一行的填写。

返回值就是dp[m][n]。

class Solution { public: int jewelleryValue(vector<vector<int>>& f) { int m=f.size(); int n=f[0].size(); vector<vector<int>> dp(m+1,vector<int>(n+1,0)); dp[1][1]=f[0][0]; 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])+f[i-1][j-1]; } } return dp[m][n]; } };

Read more

HTML5+CSS3+JavaScript实现高木同学圣诞树GalGame完整开发指南

HTML5+CSS3+JavaScript实现高木同学圣诞树GalGame完整开发指南

HTML5+CSS3+JavaScript实现高木同学圣诞树GalGame完整开发指南 摘要:圣诞节快要到了,本文详细介绍了一个基于Web技术栈开发的完整GalGame(美少女游戏)项目。从项目架构设计、核心技术实现到性能优化,全面阐述如何使用纯前端技术构建具有丰富交互功能的视觉小说游戏。项目包含完整的对话系统、角色表情管理、分支剧情和存档功能,是Web前端技术在游戏开发领域应用的优秀实践案例。 📋 目录 * 1. 项目概述与目标 * 2. 技术架构选型 * 3. 核心功能实现 * 4. 界面设计与响应式布局 * 5. 开发难点与解决方案 * 6. 性能优化与用户体验 * 7. 部署与运行指南 * 8. 扩展功能与未来展望 * 9. 总结与收获 1. 项目概述与目标 1.1 项目背景 随着HTML5、CSS3和现代JavaScript技术的快速发展,Web平台已经能够承载复杂的交互应用。GalGame作为强调剧情叙事和角色互动的游戏类型,非常适合使用Web技术来实现。本项目选择热门动漫《擅长捉弄人的高木同学》作为题材,结合圣诞节主题,开发一个温馨有趣

By Ne0inhk
【Java Web学习 | 第三篇】CSS(2) - 元素显示模式

【Java Web学习 | 第三篇】CSS(2) - 元素显示模式

🌈个人主页: Hygge_Code🔥热门专栏:从0开始学习Java | Linux学习| 计算机网络💫个人格言: “既然选择了远方,便不顾风雨兼程” 文章目录 * CSS元素显示模式 * 2.1 什么是元素显示模式? * 2.2 三大核心显示模式详解 * 1. 块级元素(Block Level Elements) * 2. 行内元素(Inline Elements) * 3. 行内块元素(Inline-Block Elements) * 2.3元素显示模式的转换语法 * 1. 转为块级元素:`display: block` * 2. 转为行内元素:`display: inline` * 3. 转为行内块元素:`display: inline-block` * 2.4 实战案例:小米侧边栏实现 * 2.

By Ne0inhk
HTML与JavaScript的羁绊

HTML与JavaScript的羁绊

HTML与JavaScript共同构成了现代Web开发的基石,它们之间的协作关系体现在以下几个方面: 一、基础定位差异 * JavaScript:赋予网页动态行为 作为解释型脚本语言,实现交互逻辑与数据操作。 HTML:定义网页的结构骨架 通过标签描述内容层级,如: <div> <button>点击</button> </div> 二、核心协作机制 1. DOM(文档对象模型)桥梁 JavaScript通过DOM接口操作HTML元素: // 获取HTML元素并修改内容 const btn = document.querySelector('.btn'); btn.textContent = "已激活"; // 动态创建新元素 const newElement = document.createElement(

By Ne0inhk
Java 中间件:RocketMQ 顺序消息(全局/分区顺序)

Java 中间件:RocketMQ 顺序消息(全局/分区顺序)

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕Java中间件这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * Java 中间件:RocketMQ 顺序消息(全局 / 分区顺序) * 什么是顺序消息? * RocketMQ 顺序消息的工作原理 * 全局顺序 vs 分区顺序 * RocketMQ 顺序消息的核心机制 * 全局顺序消息的实现 * 全局顺序的配置要求 * Java 代码示例:全局顺序消息 * 全局顺序的局限性 * 分区顺序消息的实现 * 分区顺序的设计思路 * Java 代码示例:分区顺序消息 * 分区顺序的关键要点 * 顺序消息的消费机制详解 * ConsumeOrderlyStatus 枚举 * 消费失败的处理机制 * 并发消费 vs 顺序消费

By Ne0inhk