跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

动态规划路径类 DP 入门:3 道经典例题解析

综述由AI生成动态规划路径类 DP 入门讲解了三道经典例题。包括矩阵最小路径和、迷雾森林方案数计算以及过河卒问题。重点分析了状态定义、转移方程推导、边界初始化及填表顺序。提供了 C++ 代码实现,涵盖数组越界处理、取模运算及长整型防溢出技巧,适合算法初学者理解网格 DP 模型。

墨染流年发布于 2026/3/15更新于 2026/4/255 浏览
动态规划路径类 DP 入门:3 道经典例题解析

路径类 DP 是线性 DP 的一种,它是在一个 n × m 的矩阵中设置一个行走规则,研究从起点走到终点的方案数、最小路径和或者最大路径和等问题。入门阶段的《数字三角形》其实就是路径类 DP。

矩阵的最小路径和

题目描述

在这里插入图片描述

题目解析

  1. 状态表示 dp[i][j] 表示从 [1, 1] 格子走到 [i, j] 格子时,所有方案下的最小路径和。
  2. 状态转移方程 以最后一步推导状态转移方程,走到最后一个格子 dp[n][m] 有两种情况,分别是从 dp[n - 1][m] 到 dp[n][m] 和从 dp[n][m - 1] 到 dp[n][m],所以 dp[n][m] 的取值就是取 dp[n - 1][m] 和 dp[n][m - 1] 的较小值加 a[n][m],状态转移方程如下: dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + a[i][j]
  3. 初始化 因为本题填格子时需要访问左边和上边格子,所以需要处理边界情况。在填第一行和第一列格子时会访问到第 0 行和第 0 列,但是第 0 行和第 0 列是无意义的,所以我们需要把第 0 行和第 0 列初始化为无穷大。当填表访问到第 0 行或第 0 列格子时由于状态转移方程是取两个格子较小值,所以永远不会取到第 0 行或第 0 列格子。 还需要将 dp[1][1] 初始化为 a[1][1],因为 [1, 1] 的最小路径和就是它本身,并且注意在填表时跳过 [1, 1] 格子。首先因为已经将 [1, 1] 格子初始化过了,第二如果把状态转移方程用于 [1, 1] 格子,会把 [1, 1] 格子填为无穷大。

在这里插入图片描述

  1. 填表顺序 从上往下,从左往右
  2. 输出结果 dp[n][m]

代码

#include <iostream>
#include <cstring>
using namespace std;

const int N = 510;
int n, m;
int a[N][N], dp[N][N];

int main() {
    // 处理输入
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
        }
    }
    // 初始化
    memset(dp, 0x3f3f3f3f, sizeof(dp));
    dp[1][1] = a[1][1];
    // 依序填表
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (i == 1 && j == 1) continue;
            dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + a[i][j];
        }
    }
    // 输出结果
    cout << dp[n][m] << endl;
}

迷雾森林

题目描述

在这里插入图片描述

题目解析

  1. 状态表示 dp[i][j] 表示从 [m, 1] 格子走到 [i, j] 格子时,一共有多少种方案。
  2. 状态转移方程 根据最后一步推导状态转移方程,因为题目规定只能向上或向右走,所以假设最后一个格子为 [i, j],那么上一个格子可能是 [i, j - 1] 或者 [i + 1, j],那么格子 [i, j] 的方案数就是 [i, j - 1] 格子方案数加上 [i + 1, j] 格子方案数。 但是需要注意,本题只能走空地,如果遇到树是不能走的,也就是只有空地可以用状态转移方程填表,森林格子还是保持默认初始值 0。 所以状态转移方程为: a[i][j] = 0 --> dp[i][j] = dp[i][j - 1] + dp[i + 1][j] a[i][j] = 1 --> dp[i][j] = 0

在这里插入图片描述

  1. 初始化
    • 因为本题填表时会访问左边格子和下边格子,所以需要将 dp 数组第 0 列和第 0 行格子初始化为 0,因为 dp 数组在全局开辟,所以值默认为 0,不需要手动用 memset 初始化。
    • 因为本题是求解方案数,后续格子方案数就是从第一个格子累加而来,所以将 dp[m][1] 初始化为 1(注意:填表时不填 [m][1] 格子)
  2. 填表顺序 因为根据前面推导的状态转移方程填表时需要访问左边格子和下边格子,所以填表顺序是从下往上填每一行,填每一行时遵循从左往右。
  3. 输出结果 dp[1][n]

注意:本题规定答案需要对 2333 取模。

代码

#include <iostream>
#include <stdio.h>
using namespace std;

const int N = 3010;
const int MOD = 2333;
int m, n;
int a[N][N], dp[N][N];

int main() {
    // 处理输入
    cin >> m >> n;
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            scanf("%d", &a[i][j]);
        }
    }
    // 初始化
    dp[m][1] = 1;
    // 按序填表
    for (int i = m; i >= 1; i--) {
        for (int j = 1; j <= n; j++) {
            // [m][1] 格子以及初始化,无需再次填
            if (i == m && j == 1) continue;
            if (a[i][j] == 0) dp[i][j] = (dp[i][j - 1] + dp[i + 1][j]) % MOD;
        }
    }
    // 输出结果
    cout << dp[1][n];
    return 0;
}

过河卒

题目描述

在这里插入图片描述

题目解析

由于本题强制规定从 [0, 0] 格子开始填表,所以为了方便处理数组越界问题,我们将题目输入的 B 点坐标和马的坐标统一加 1,这样就相当于将整个棋盘往下和往右移了一格,就可以从 [1, 1] 格子开始填表。

  1. 状态表示 dp[i][j] 表示从 [1, 1] 格子走到 [i, j] 格子时,一共有多少种方案。
  2. 状态转移方程 根据最后一步推导状态转移方程,题目规定卒行走的规则只能向下、或者向右,所以状态转移方程: dp[i][j] = dp[i][j - 1] + dp[i - 1][j] 但是需要注意只有走到不是马的控制点时才能用状态转移方程填表,如果走到马的控制点不做任何操作,让它保持初始值 0 即可。
  3. 初始化 本题需要对 a、dp 数组分别初始化,dp 数组将 [1][1] 置为 1 即可,因为方案数问题的答案需要从第一个格子开始累加得到。 a 数组的处理思路是将马的 9 个控制点全部置为 1,其余点保持 0,在后面根据状态转移方程填表时需要判断 a 数组对应格子是否为 0,如果为 0 可以填表,不为 0 则直接 continue,不做任何操作。本题对 a 数组初始化我们借助方向向量实现。
  4. 填表顺序 从左往右,从上往下
  5. 输出结果 dp[n][m]

注意:

  1. 马所在的点也是马的控制点。
  2. dp 数组要用 long long 类型,因为方案数类型题目的数据是阶层级别的。

代码

#include <iostream>
using namespace std;

typedef long long LL;
const int N = 25;
int n, m, c, d;
int a[N][N];
LL dp[N][N];

// 方向向量
int dx[8] = {2, 2, 1, 1, -1, -1, -2, -2};
int dy[8] = {1, -1, 2, -2, 2, -2, 1, -1};

int main() {
    // 处理输入
    cin >> n >> m >> c >> d;
    n++, m++, c++, d++;
    // 初始化
    dp[1][1] = 1;
    for (int i = 0; i < 8; i++) {
        a[c + dx[i]][d + dy[i]] = 1;
    }
    a[c][d] = 1;
    // 按序填表
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (i == 1 && j == 1) continue;
            if (a[i][j] == 0) dp[i][j] = dp[i][j - 1] + dp[i - 1][j];
        }
    }
    // 输出结果
    cout << dp[n][m] << endl;
    return 0;
}

目录

  1. 矩阵的最小路径和
  2. 迷雾森林
  3. 过河卒
  • 💰 8折买阿里云服务器限时8折了解详情
  • 💰 8折买阿里云服务器限时8折购买
  • 🦞 5分钟部署阿里云小龙虾了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • SpringBoot + Vue 前后端分离项目实战:权限、工作流与报表
  • 基于 AI 辅助快速开发 MC.JS WEBMC 1.8.8 移动端插件实践
  • AI 时代人才新逻辑:深度专业与跨界能力的复合进化
  • 2026 年 6 款免费 AI 写作工具实测与避坑指南
  • AI 创作者崛起:掌握核心工具,在 AMA 互动中共同成长
  • 具身智能落地瓶颈:RoboBrain 2.5 的空间与时间协同方案
  • Spring Boot 集成 Debezium 实现 PostgreSQL 增量同步
  • pg_lake 核心功能:Parquet/CSV/JSON 文件查询与导入技巧
  • VSCode Copilot 登录异常排查与修复指南
  • 基于昇腾 NPU 的 CodeLlama 模型部署与推理实践
  • Whisper 和 Faster-Whisper 模型下载与安装方法
  • 初阶数据结构:顺序表
  • MySQL JDBC 编程基础
  • Tabnine、Cursor 与 Copilot:三款 AI 编程助手效率对比
  • 递归与回溯算法专题:汉诺塔、链表操作及快速幂
  • SystemVerilog 全面教程:从基础到高级验证
  • Hello World 背后的启动逻辑
  • AI 产品经理工作全流程与模型构建实战指南
  • Jetpack Compose 完全开发手册:从入门到精通
  • 2024 年 3 月编程语言排行榜:Python 优势显著,Rust 持续上升

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online