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

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

动态规划核心在于状态定义与转移方程的推导。本文选取泰波那契数、爬楼梯及解码方法等四道经典题目,演示如何从状态表示入手,结合初始化与填表顺序,逐步构建一维 DP 解法。内容涵盖空间优化技巧及边界处理细节,适合算法初学者建立解题直觉。

小熊软糖发布于 2026/3/27更新于 2026/10/2594 浏览
C/C++ 算法入门:一维动态规划基础实战

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

动态规划核心概念

动态规划(Dynamic Programming)本质上是利用空间换时间,通过记录子问题的解来避免重复计算。理解它的关键在于将问题拆解为状态定义、状态转移方程、初始化和填表顺序。

简单来说,动态规划可以理解为:某种状态的公式 + 提前求出来值的容器。我们求出当前位置的值,放入容器中供后续使用。因为初始值通常是可见的,所以能启动整个推导过程。

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

  • 状态定义:明确 dp[i] 的含义。
  • 状态转移方程:描述如何从已知状态推导出当前状态。
  • 初始条件:确定边界值,防止越界。
  • 状态存储:通常用数组或滚动变量实现。

解题通用步骤

在实际做题中,建议遵循以下逻辑流,而不是机械地套用模板:

  1. 状态表示:分析题目,确定 dp[i] 代表什么(例如:以 i 结尾的最优解)。
  2. 状态转移方程:思考 dp[i] 与前面状态的关系(如 dp[i-1], dp[i-2]),写出递推式。
  3. 初始化:根据题目要求设置起始值,确保递推不越界。
  4. 填表顺序:保证计算当前状态时,依赖的状态已经计算完成(通常从左到右或从上到下)。
  5. 返回值:根据题意从 DP 表中提取最终结果。

提示:状态表示通常为'以 i 位置结尾/开头'。初始化通常关注第一个状态,而返回值关注最后一个状态。处理数字字符判断时,务必转换为数值再比较,避免逻辑漏洞。


实战演练

下面通过四道经典题目,演示如何将上述思路落地。

1. 第 N 个泰波那契数 (Tribonacci Number)

题目链接:LeetCode 1137

思路分析: 这是一个典型的线性递推问题。我们需要找到前三个数来计算当前的数。

  • 状态表示:dp[i] 表示第 i 个泰波那契数。
  • 转移方程:dp[i] = dp[i-1] + dp[i-2] + dp[i-3]。
  • 初始化:dp[0]=0, dp[1]=1, dp[2]=1。
  • 空间优化:由于只需要前三个数,可以使用三个变量代替数组,将空间复杂度从 O(N) 降为 O(1)。

代码实现:

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

2. 三步问题 (Three Steps Problem)

题目链接:LeetCode LCCI 08.09

思路分析: 小孩每次可以走 1、2 或 3 步。到达第 n 阶的方法数等于到达前 3 阶方法数之和。

  • 状态表示:dp[i] 表示到达第 i 阶台阶的方法数。
  • 转移方程:dp[i] = dp[i-1] + dp[i-2] + dp[i-3]。
  • 初始化:
    • dp[0] = 1 (原地不动算一种方式)
    • dp[1] = 1
    • dp[2] = 2
  • 注意:结果可能需要取模,防止整数溢出。

代码实现:

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. 使用最小花费爬楼梯 (Min Cost Climbing Stairs)

题目链接:LeetCode 746

思路分析: 这道题有两种常见的 DP 视角:

视角一:到达某一层的最小花费

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

视角二:从某一层出发到达楼顶的最小花费

  • 状态表示:dp[i] 表示从第 i 层出发到达楼顶的最小花费。
  • 转移方程:dp[i] = cost[i] + min(dp[i+1], dp[i+2])。
  • 填表顺序:从右向左。

代码实现(视角一):

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int n = cost.size();
        // dp[i] 表示到达第 i 层需要的最小花费
        vector<int> dp(n + 1);
        
        // 初始化:前两阶可以直接上,花费即为对应台阶的费用
        dp[0] = cost[0];
        dp[1] = cost[1];
        
        for (int i = 2; i <= n; ++i) {
            if (i == n) {
                // 到达楼顶不需要支付额外费用,只需从下两层跳上来
                dp[i] = min(dp[i-1], dp[i-2]);
            } else {
                dp[i] = min(dp[i-1], dp[i-2]) + cost[i];
            }
        }
        return dp[n];
    }
};

4. 解码方法 (Decode Ways)

题目链接:LeetCode 91

思路分析: 这是一个字符串解码问题,涉及边界条件的复杂判断。

  • 状态表示:dp[i] 表示以第 i 个字符结尾的解码方法总数。
  • 转移方程:
    1. 单个字符解码(1-9):dp[i] += dp[i-1]
    2. 两个字符组合解码(10-26):dp[i] += dp[i-2]
  • 初始化技巧:为了简化边界处理,可以在 DP 数组前多开一位虚拟节点,设 dp[0] = 1。
  • 注意:遇到 '0' 需要特殊处理,不能单独作为有效数字。

代码实现:

class Solution {
public:
    int numDecodings(string s) {
        int n = s.size();
        // dp[i] 表示前 i 个字符的解码方法数
        // 多开一位用于处理边界,dp[0] 设为 1 作为虚拟起点
        vector<int> dp(n + 1, 0);
        dp[0] = 1;
        
        for (int i = 1; i <= n; ++i) {
            // 单个字符判断
            if (s[i-1] != '0') {
                dp[i] += dp[i-1];
            }
            
            // 两个字符组合判断
            if (i >= 2) {
                int val = (s[i-2] - '0') * 10 + (s[i-1] - '0');
                if (val >= 10 && val <= 26) {
                    dp[i] += dp[i-2];
                }
            }
        }
        
        return dp[n];
    }
};

总结

掌握一维动态规划的关键在于抽象能力。不要死记硬背公式,而是尝试从题目的具体约束中提炼出状态之间的关系。通过大量练习,你会逐渐形成对'状态'和'转移'的直觉,从而能够应对更复杂的二维或多维动态规划问题。

目录

  1. C/C++ 算法入门:一维动态规划基础实战
  2. 动态规划核心概念
  3. 解题通用步骤
  4. 实战演练
  5. 1. 第 N 个泰波那契数 (Tribonacci Number)
  6. 2. 三步问题 (Three Steps Problem)
  7. 3. 使用最小花费爬楼梯 (Min Cost Climbing Stairs)
  8. 4. 解码方法 (Decode Ways)
  9. 总结

更多推荐文章

查看全部
  • CNN 经典架构演进与 PyTorch 实战指南
  • Python 大麦网自动化抢票技术实现指南
  • 新机型 Copilot 键替代右 Ctrl 键的解决方案
  • Playwright 核心能力与实战笔记
  • Consul Template 配置文件自动替换实战
  • IT 项目管理那些事儿
  • 数字化 X 射线摄影系统几何精度质量控制体模研究
  • Python 爬取同花顺股票数据及技术指标提取
  • AI 时代产品经理成长之路与核心能力模型构建
  • 基于大型语言模型的智能 Agent:发展历程、架构与 Langchain 实现
  • 利用 DeepSeek 辅助开发贪吃蛇游戏实战
  • OpenClaw 配置本地 Ollama 模型指南:打造全离线个人 AI 助理
  • Antigravity 配置指南:打造类 VS Code 的 AI IDE
  • Web 视频解码器性能优化:从 136KB 到 20KB 的压缩实践
  • Axum:Rust 高性能 Web 框架实战指南
  • AI 辅助游戏开发:使用 DeepSeek 构建贪吃蛇游戏
  • Linux 系统安装、故障排除、权限管理与安全配置指南
  • Stable Diffusion 完整训练与推理流程详解(附伪代码)
  • 后端开发与前端开发的难度对比:一名后端转前端的真实体验
  • Stable Diffusion v1.5 Archive 与 SDXL-Lightning 生成速度与质量对比

相关免费在线工具

  • 加密/解密文本

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