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

C/C++ 算法入门:一维动态规划基础实战

一维动态规划的核心在于状态定义与状态转移方程的构建。通过四个经典例题——泰波那契数、三步问题、最小花费爬楼梯及解码方法,系统讲解动规五步法:状态表示、转移方程、初始化、填表顺序及返回值。重点涵盖边界处理技巧、空间优化(滚动数组)以及虚拟节点的应用,帮助读者掌握从基础到进阶的一维 DP 解题思路。

神经兮兮发布于 2026/2/17更新于 2026/4/255 浏览
C/C++ 算法入门:一维动态规划基础实战

C/C++ 算法入门:一维动态规划基础实战

动态规划核心思路

动态规划(DP)本质上是某种状态的公式加上提前求好值的容器。通过计算当前位置的值并存储到容器中供后续使用,从而避免重复计算。

严格来说,动态规划包含四个要素:

  • 状态定义:明确 dp[i] 的含义。
  • 状态转移方程:推导 dp[i] 与之前状态的关系。
  • 初始条件:确定边界值。
  • 状态存储:通常使用数组或滚动变量。

解题五步法

在实际刷题中,建议遵循以下流程:

  1. 状态表示:确定 dp 表中每个元素的含义。通常以'以 i 位置结尾'或'以 i 位置开始'为切入点。
  2. 状态转移方程:这是最难的一步,需结合题目含义推导 dp[i] = ?。
  3. 初始化:确保填表时不越界,根据题目要求设定初始值。
  4. 填表顺序:保证计算当前状态时,依赖的前置状态已计算完成(通常从左向右或从上到下)。
  5. 返回值:根据题意从 dp 表中提取最终结果。

提示:状态表示常涉及'以 i 位置结尾/开头'。初始化通常分析第一个状态,而返回值分析最后一个状态。处理数字字符判断时,务必转换为数值而非直接比较字符,以免出现不可控情况。必要时可开辟额外空间处理边界问题。


实战演练

下面通过四道经典题目,逐步应用上述步骤。

1. 第 N 个泰波那契数

题目描述:T(0) = 0, T(1) = 1, T(2) = 1, T(n) = T(n-1) + T(n-2) + T(n-3)。

分析:

  • 状态表示:dp[i] 表示第 i 个泰波那契数。
  • 转移方程:dp[i] = dp[i-1] + dp[i-2] + dp[i-3]。
  • 初始化:dp[0]=0, dp[1]=1, dp[2]=1。
  • 填表顺序:从左向右。
  • 返回值:dp[n]。

空间优化:由于只需要前三个状态,可以使用三个变量代替数组。

class Solution {
public:
    int tribonacci(int n) {
        // 空间优化写法
        if (n == 0) return 0;
        if (n == 1 || n == 2) return 1;
        
        int a = 0, b = 1, c = 1;
        int ret = 0;
        for (int i = 3; i <= n; i++) {
            ret = a + b + c;
            a = b;
            b = c;
            c = ret;
        }
        return ret;
    }
};

2. 三步问题

题目描述:小孩每次可以走 1、2 或 3 步,问到达 n 阶台阶有多少种方法?结果对 10^9+7 取模。

分析:

  • 状态表示:dp[i] 表示到达第 i 阶的方法数。
  • 转移方程:可以从 i-1、i-2 或 i-3 走过来,故 dp[i] = dp[i-1] + dp[i-2] + dp[i-3]。
  • 初始化:
    • dp[0] = 1(不动也算一种方式)
    • dp[1] = 1
    • dp[2] = 2
  • 注意:需处理 n 较小的情况,且加法时需防止溢出,使用 long long 转换后取模。
class Solution {
public:
    int waysToStep(int n) {
        if (n == 1) return 1;
        if (n == 2) return 2;
        if (n == 3) return 4;
        
        vector<int> dp(n + 1);
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 4;
        
        for (int i = 4; i <= n; i++) {
            dp[i] = ((long long)dp[i - 1] + dp[i - 2] + dp[i - 3]) % 1000000007;
        }
        return dp[n];
    }
};

3. 使用最小花费爬楼梯

题目描述:给定 cost 数组,cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦支付费用,你可以选择爬一步或两步。求到达楼顶的最小花费。

解法一:正向推导

  • 状态表示:dp[i] 表示到达第 i 层所需的最小花费。
  • 转移方程:dp[i] = min(dp[i-1], dp[i-2]) + cost[i]。
  • 初始化:dp[0] = cost[0], dp[1] = cost[1]。
  • 返回值:dp[n](楼顶在数组末尾之后)。

解法二:反向推导

  • 状态表示:dp[i] 表示从第 i 阶出发到达楼顶的最小花费。
  • 转移方程:dp[i] = cost[i] + min(dp[i+1], dp[i+2])。
  • 初始化:dp[n-1] = cost[n-1], dp[n-2] = cost[n-2]。
  • 返回值:min(dp[0], dp[1])。
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int n = cost.size();
        vector<int> dp(n + 1);
        
        // 初始化前两层
        dp[0] = cost[0];
        dp[1] = cost[1];
        
        for (int i = 2; i < n; i++) {
            dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i];
        }
        
        // 最后一步可以直接从 n-1 或 n-2 跨出,无需再付 cost[n]
        return min(dp[n - 1], dp[n - 2]);
    }
};

4. 解码方法

题目描述:字符串仅由数字组成,'A'->1, 'B'->2... 'Z'->26。求解码方法的总数。

分析:

  • 状态表示:dp[i] 表示以 i 结尾的解码方法总数。
  • 转移方程:
    1. 单个字符解码:若 s[i] 在 1-9 之间,dp[i] += dp[i-1]。
    2. 两个字符组合:若 s[i-1]s[i] 在 10-26 之间,dp[i] += dp[i-2]。
  • 边界处理技巧:多开一位虚拟节点 dp[0]=1,方便处理 dp[2] 依赖 dp[0] 的情况。
class Solution {
public:
    int numDecodings(string s) {
        int n = s.size();
        vector<int> dp(n + 1);
        
        // 虚拟节点初始化
        dp[0] = 1;
        
        // 处理第一个字符
        if (s[0] != '0') dp[1] = 1;
        
        for (int i = 2; i <= n; i++) {
            // 单个字符判断
            int oneChar = s[i - 1] - '0';
            if (oneChar >= 1 && oneChar <= 9) {
                dp[i] += dp[i - 1];
            }
            
            // 两个字符组合判断
            int twoChars = (s[i - 2] - '0') * 10 + oneChar;
            if (twoChars >= 10 && twoChars <= 26) {
                dp[i] += dp[i - 2];
            }
        }
        
        return dp[n];
    }
};

总结

通过以上练习,我们可以总结出一些通用经验:

  1. 状态表示要清晰,通常关注'以 i 结尾'或'以 i 开始'。
  2. 边界条件是动规最容易出错的地方,建议多开一位或使用虚拟节点简化逻辑。
  3. 空间优化:当只依赖前几个状态时,可用滚动变量将空间复杂度降为 O(1)。
  4. 类型安全:涉及累加时注意整数溢出,及时使用 long long 或取模。

掌握这些基础套路,面对更复杂的一维 DP 问题时也能做到心中有数。

目录

  1. C/C++ 算法入门:一维动态规划基础实战
  2. 动态规划核心思路
  3. 解题五步法
  4. 实战演练
  5. 1. 第 N 个泰波那契数
  6. 2. 三步问题
  7. 3. 使用最小花费爬楼梯
  8. 4. 解码方法
  9. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • 💰 8折买阿里云服务器限时8折购买
  • 🦞 5分钟部署阿里云小龙虾了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • AI 大模型、Agent 模式、自定义知识库与 LangChain 解析
  • AMD显卡AI绘画终极指南:解锁ComfyUI-Zluda隐藏性能
  • 基于 YOLO12 的无人机航拍视角目标检测系统
  • 开源知识库 RAGFlow 从部署到实战操作详解
  • AIGC 个性化与定制化内容生成:技术与应用
  • 本地 AI 服务远程管理的加密隧道解决方案
  • C++ STL map与set底层结构及迭代器实现详解
  • Web 安全学习框架搭建与常见漏洞解析
  • KingbaseES SQL 防火墙的工程化实践与性能分析
  • LLama-Factory 集成 HuggingFace 镜像加速模型下载与训练
  • Android 开发核心技术体系与面试高频考点总结
  • AI 大模型技术白皮书:发展趋势、挑战与展望
  • 实测 ToClaw 信息检索与分析能力:AI 实现“先找再写”
  • 多模态 AI 应用:图文音视频一体化开发实战
  • 基于 LangChain-Chatchat 实现本地知识库问答应用快速上手
  • Python Tkinter Frame 核心解析与实战指南
  • 元气 AI Bot 安装部署与飞书集成使用指南
  • Java 中的 ConcurrentLinkedQueue 详解
  • 通义万相 2.1 视频生成模型特性与算力平台概述
  • FPGA 任意角度图像旋转实现原理与代码设计

相关免费在线工具

  • 加密/解密文本

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