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

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

目录

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

Flutter 三方库 wasm_ffi 深入鸿蒙端侧硬核 WebAssembly 虚拟机沙盒穿透适配全景:通过异步极速 FFI 中继管道打通底层高算力异构服务-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 wasm_ffi 深入鸿蒙端侧硬核 WebAssembly 虚拟机沙盒穿透适配全景:通过异步极速 FFI 中继管道打通底层高算力异构服务-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 wasm_ffi 深入鸿蒙端侧硬核 WebAssembly 虚拟机沙盒穿透适配全景:通过异步极速 FFI 中继管道打通底层高算力异构服务并全面实现无损语言壁垒交互 前言 在 OpenHarmony 应用向高性能计算领域扩展的过程中,如何优雅地接入已有的 C/C++ 算法库(如加密引擎、重型图像处理、数学模拟)而又不失跨平台的便捷性?传统的 NAPI 虽然稳健,但在 Flutter 生态中,直接利用 WebAssembly (WASM) 配合 FFI(External Function Interface)的语义可以在一定程度上实现代码的高度复用。wasm_ffi 库为 Flutter 开发者提供了一套在 Dart 环境下调用 WASM

By Ne0inhk
三种适用于Web版IM(即时通讯)聊天信息的加密算法实现方案

三种适用于Web版IM(即时通讯)聊天信息的加密算法实现方案

文章目录 * **第一部分:引言与核心密码学概念** * **1.1 为什么IM需要端到端加密(E2EE)?** * **1.2 核心密码学概念与工具** * **第二部分:方案一:静态非对称加密(基础方案)** * **2.1 方案概述与流程** * **2.2 前端Vue实现(使用node-forge)** * **1. 安装依赖** * **2. 核心工具类 `crypto.js`** * **3. Vue组件中使用** * **2.3 后端Java实现(Spring Boot)** * **1. 实体类** * **2. Controller层** * **3. WebSocket配置** * **2.4 密钥管理、注册与登录集成** * **1. 用户注册/登录时生成密钥** * **2. 密钥设置页面** * **2.

By Ne0inhk
前端代码生成的大洗牌:当 GLM 4.7 与 MiniMax 挑战 Claude Opus,谁才是性价比之王?

前端代码生成的大洗牌:当 GLM 4.7 与 MiniMax 挑战 Claude Opus,谁才是性价比之王?

在 AI 辅助编程领域,长期以来似乎存在一条不成文的铁律:如果你想要最好的结果,就必须为最昂贵的模型买单(通常是 Anthropic 或 OpenAI 的旗舰模型)。然而,随着国产大模型如 GLM 4.7 和 MiniMax M2.1 的迭代,这一格局正在发生剧烈震荡。 最近,一场针对Claude Opus 4.5、Gemini 3 Pro、GLM 4.7 和 MiniMax M2.1 的前端 UI生成横向测评,打破了许多人的固有认知。在这场包含落地页、仪表盘、移动端应用等五个真实场景的较量中,不仅出现了令人咋舌的“滑铁卢”,更诞生了性价比极高的“新王”。 本文将深入拆解这场测试的细节,透过代码生成的表象,探讨大模型在工程化落地中的真实效能与成本逻辑。

By Ne0inhk
【Java Web学习 | 第14篇】JavaScript(8) -正则表达式

【Java Web学习 | 第14篇】JavaScript(8) -正则表达式

🌈个人主页: Hygge_Code🔥热门专栏:从0开始学习Java | Linux学习| 计算机网络💫个人格言: “既然选择了远方,便不顾风雨兼程” 文章目录 * JavaScript 正则表达式详解 * 什么是正则表达式🤔 * JavaScript 正则表达式的定义与使用🥝 * 1. 字面量语法 * 2. 常用匹配方法 * test() 方法🍋‍🟩 * exec() 方法🍋‍🟩 * 正则表达式的核心组成部分🐦‍🔥 * 1. 元字符 * 边界符 * 量词 * 字符类 * 2. 修饰符 * 简单示例🍂 JavaScript 正则表达式详解 正则表达式是处理字符串的强大工具,在 JavaScript 中被广泛应用于表单验证、文本处理和数据提取等场景。本文将从正则表达式的基本概念出发,详细介绍其语法规则和实际应用方法。 什么是正则表达式🤔 正则表达式是用于匹配字符串中字符组合的模式,在 JavaScript

By Ne0inhk