跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

线性动态规划经典例题解析

线性动态规划是基础且常见的 DP 类型,状态转移依赖前序状态。通过台阶问题、最大子段和、传球游戏及乌龟棋四个经典案例,详细讲解了一维、二维及多维 DP 的状态定义、转移方程推导及边界处理技巧,涵盖模运算优化、空间压缩及环形数组处理等实战要点。

SparkGeek发布于 2026/3/15更新于 2026/6/2026 浏览
线性动态规划经典例题解析

线性动态规划是动态规划问题中最基础、最常见的一类。它的特点是状态转移只依赖于前一个或前几个状态,状态之间的关系是线性的,通常可以用一维或者二维数组来存储状态。我们在入门阶段解决的《下楼梯》以及《数字三角形》其实都是线性 DP,一个是一维的,另一个是二维的。下面我们通过四个经典题目来深入理解这一类问题的解法。

台阶问题

本题可以看作是《下楼梯》问题的加强版,总体思路不变。按照动态规划的常规步骤来分析这道题。

状态表示

dp[i] 表示走到第 i 个台阶的所有方案数。

状态转移方程

第 i 个台阶的方案数等于从 i-1 阶到 i-k 阶的所有方案数之和。由于数据较大,结果需要对 100003 取模。需要注意的是,访问台阶时需要保证 i-j 始终大于等于 0,防止负下标越界。

dp[i] = (dp[i] + dp[i - j]) % MOD; // j 从 1 到 k

初始化与填表

直接初始化 dp[0] = 1 即可,然后从 dp[1] 开始往后循环计算。填表顺序从左往右。最终结果即为 dp[n]。

#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10, MOD = 1e5 + 3;
int n, k;
LL dp[N];

int main() {
    cin >> n >> k;
    // 初始化
    dp[0] = 1;
    // 循环填表
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= k; j++) {
            // 保证不越界访问
            if (i - j < 0) break;
            dp[i] = (dp[i] + dp[i - j]) % MOD;
        }
    }
    // 输出结果
    cout << dp[n] << endl;
    return 0;
}

最大子段和

在处理子序列和子数组问题时,定义状态表示有一个技巧:以某个位置为结尾,结合题意来定义。

状态表示

f[i] 表示以 i 位置为结尾的所有子数组中,最大和是多少。

状态转移方程

根据最后一步划分情况。如果 n 为 1,最大子段就是它本身。如果 n 大于 1,第 i 个格子的最大子段和有两种可能:

  1. 最大子段和是它本身 a[i],因为之前的最大子段和可能是负数。
  2. 最大子段和是第 i 个格子之前的所有子段中的最大子段加上第 i 个格子的数据。 综合起来:
f[i] = max(a[i], f[i - 1] + a[i]);

初始化与结果

将 f[0] 初始化为 0 即可满足要求。填表顺序从左往右。结果为 f 数组中的最大值。为了节省空间,可以在输入时直接更新 f 数组并维护最大值。

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2e5 + 10;
int n;
int f[N];

int main() {
    cin >> n;
    // 初始化
    f[0] = 0;
    int ret = -0x3f3f3f3f;
    // 按序填表 + 更新 ret
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        f[i] = max(x, x + f[i - 1]);
        ret = max(ret, f[i]);
    }
    cout << ret << endl;
    return 0;
}

传球游戏

题目规定所有同学站成一个圆圈,球可能从左边格子来也可能从右边格子来。需要知道该格子编号和传球次数,因此使用二维数组 f[i][j]。

状态表示

f[i][j] 表示球传递了 i 次之后,最终落在了 j 号同学手里一共有多少种方案。

状态转移方程

由于是环形结构,需要分三种情况讨论:

  1. 接受球的是编号为 1 的同学,来源可能是 2 和 n。 dp[i][1] = dp[i - 1][2] + dp[i - 1][n]
  2. 接受球的是编号为 2 到 n-1 的同学 j,来源可能是 j-1 和 j+1。 dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1]
  3. 接受球的是编号为 n 的同学,来源可能是 1 和 n-1。 dp[i][n] = dp[i - 1][1] + dp[i - 1][n - 1]

初始化与填表

第一行表示传球次数为 0,此时球在一号同学手里,所以 dp[0][1] = 1。填表顺序从上往下依次填每一行。最终结果是 dp[m][1]。

#include <iostream>
using namespace std;
const int N = 40;
int n, m;
int dp[N][N];

int main() {
    cin >> n >> m;
    // 初始化
    dp[0][1] = 1;
    // 按序填表
    for (int i = 1; i <= m; i++) {
        // 填第 1 个同学
        dp[i][1] = dp[i - 1][2] + dp[i - 1][n];
        // 填第 2 到 n-1 个同学
        for (int j = 2; j < n; j++) {
            dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1];
        }
        // 填第 n 个同学
        dp[i][n] = dp[i - 1][1] + dp[i - 1][n - 1];
    }
    cout << dp[m][1] << endl;
    return 0;
}

乌龟棋

本题类似飞行棋,抽卡片决定走多少步,目标是找出从起点走到终点格子的最大分数。

状态表示

需要记录走到某格子时,用 1 卡片 i 张、2 卡片 j 张、3 卡片 k 张、4 卡片 z 张的情况下的最大分数。虽然本质涉及五维信息,但一维数组下标 x 可以通过另外 4 个变量计算出来:x = 1 + i + 2*j + 3*k + 4*z。因此使用四维 dp 数组存储。

状态转移方程

走到 x 一共有四种可能:分别是从 x-1 到 x、x-2 到 x、x-3 到 x、x-4 到 x。对应消耗不同种类的卡片。 例如从 x-1 过来,消耗一张 1 卡片,状态为 dp[i-1][j][k][z]。 注意边界条件,访问 dp[i-1] 时需保证 i > 0。

t = max(t, dp[i - 1][j][k][z] + a[x]);

同理处理其他三种卡片。

初始化与填表

题目规定乌龟棋子自动获得起点格子的分数,所以 dp[0][0][0][0] = a[1]。 代码实现中用 4 个 for 循环依次枚举 i, j, k, z 的所有取值,取出最大值作为当前状态的取值。最终结果是 dp[cnt[1]][cnt[2]][cnt[3]][cnt[4]]。

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 360;
const int M = 50;
int n, m;
int a[N], cnt[5], dp[M][M][M][M];

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    while (m--) {
        int x;
        cin >> x;
        cnt[x]++;
    }
    // 初始化
    dp[0][0][0][0] = a[1];
    // 按顺序填表
    for (int i = 0; i <= cnt[1]; i++) {
        for (int j = 0; j <= cnt[2]; j++) {
            for (int k = 0; k <= cnt[3]; k++) {
                for (int z = 0; z <= cnt[4]; z++) {
                    int x = 1 + i + 2 * j + 3 * k + 4 * z;
                    int& t = dp[i][j][k][z];
                    if (i > 0) t = max(t, dp[i - 1][j][k][z] + a[x]);
                    if (j > 0) t = max(t, dp[i][j - 1][k][z] + a[x]);
                    if (k > 0) t = max(t, dp[i][j][k - 1][z] + a[x]);
                    if (z > 0) t = max(t, dp[i][j][k][z - 1] + a[x]);
                }
            }
        }
    }
    cout << dp[cnt[1]][cnt[2]][cnt[3]][cnt[4]] << endl;
    return 0;
}

目录

  1. 台阶问题
  2. 状态表示
  3. 状态转移方程
  4. 初始化与填表
  5. 最大子段和
  6. 状态表示
  7. 状态转移方程
  8. 初始化与结果
  9. 传球游戏
  10. 状态表示
  11. 状态转移方程
  12. 初始化与填表
  13. 乌龟棋
  14. 状态表示
  15. 状态转移方程
  16. 初始化与填表
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • AI_NovelGenerator 本地部署与使用教程
  • C++ 核心概念:引用、内联与 nullptr 解析
  • OpenManus 开源自主规划智能体详解
  • FPGA 实现 CIC 抽取滤波器
  • Llama-Factory 微调常见错误及解决方案
  • node-llama-cpp 错误处理与调试:本地 AI 开发常见问题
  • DeepSeek 深度使用指南与高效提示词技巧
  • Ubuntu 环境下 JDK 1.8 环境变量配置指南
  • AI 编程实战:自动化代码生成、低代码与算法优化
  • 人工智能应用工程师(高级)课程体系与实战路径解析
  • DeepSeek-R1-Distill-Llama-8B 模型本地部署与高性能推理服务搭建
  • Flutter serial 库鸿蒙化适配:Web 串口通信与硬件连接指南
  • LangChain 提示模板详解:从基础到进阶
  • Python 的十大核心特性详解
  • AI 绘画动画快速上手指南:让静态涂鸦动起来
  • Windows 环境下 llama.cpp 编译与 Qwen 模型本地部署指南
  • 智谱 GLM-5 旗舰模型开源及昇腾部署指南
  • 基于 FastAPI 的 Web 上位机系统设计与实战
  • Three.js 中 GPGPU 水面模拟示例代码深度解析
  • 通义万相 2.1 图生视频技术测评与部署指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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