《LeetCode 746 使用最小花费爬楼梯 || LeetCode 91 解码方法》

一、题目

二、做题思路

2.1 状态表示(核心基础)

本题要求计算到达每一级台阶 所需的最小花费。我们定义 dp[i] 表示到达第 i 级台阶(即下标为 i 的位置)时的最小累计花费 。注意,楼梯顶部对应于下标为 nn 为数组长度),即越过最后一个台阶。该定义以位置为结尾 进行统计,是后续推导的出发点

2.2 状态转移方程(关键难点)

可以选择向上爬 1 个台阶或 2 个台阶 ,因此到达第 i 级台阶的最后一步 要么来自 i-1(支付 cost[i-1]),要么来自 i-2(支付 cost[i-2])。为了花费最小 ,我们在两者中取较小值:

dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2]) (其中 i ≥ 2)。

该方程将全局最优 拆解为子问题最优,是动态规划的核心

2.3 初始化(边界防护)

题目允许从下标 0 或 1 的台阶开始 ,意味着到达这两个位置无需支付任何费用 ,因此:

dp[0] = 0dp[1] = 0

同时,cost 数组中的费用仅用于离开该台阶时支付,与到达时的累计花费无关,初始化必须符合这一语义。

2.4 填表顺序(递推方向)

计算 dp[i] 必须依赖 dp[i-1]dp[i-2],这些下标均小于 i。因此必须从左到右 (即下标从小到大)依次填充 dp 表,保证每个状态计算时,其所有前置状态均已就绪

2.5 返回值(目标映射)

题目要求返回到达楼梯顶部的最小花费 ,而楼梯顶部对应下标 nn = cost.length),dp[n] 正好表示该值。因此直接返回 dp[n]

三、代码

cpp 复制代码
#include <vector>
using namespace std;
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost)
    {
        int n = cost.size();
        //1.创建dp表
        vector<int> dp(n + 1);

        //3. 初始化
        dp[0] = dp[1] = 0;//可以从0和1开始爬因此不需要交钱

        //2.状态转移方程
        for (int i = 2; i <= n; i++)
        {
            //4.填写顺序从左往右
            dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);//求上一步和上上步谁花钱最少到这
        }
        return dp[n];
    }
};

四、流程图

五、题目

六、做题思路

6.1 状态表示(核心基础)

本题要求计算给定字符串 s 的解码方法总数 。我们定义 dp[i] 表示字符串前 i 个字符(即 s[0..i-1])的解码方法数 。该定义以前缀长度 为状态,直接对应题目所求,是后续推导的出发点

6.2 状态转移方程(关键难点)

解码第 i 个字符(位置 i-1)时,最后一步有两种可能:

  • 单独解码 s[i-1],要求该字符不为 '0' ,此时贡献 dp[i-1]

  • 与前一字符组合 ,要求 s[i-2] 不为 '0' 且两位数字在 10 ~ 26 之间,此时贡献 dp[i-2]

因此,方程可写为 dp[i] = dp[i-1] + dp[i-2] ,但两项仅在对应条件满足时才累加,具体为:

  • s[i-1] != '0',则 dp[i-1] 有效;否则此项为 0。

  • s[i-2] != '0'(s[i-2]-'0')*10 + (s[i-1]-'0') <= 26,则 dp[i-2] 有效;否则此项为 0。

6.3 初始化(边界防护)

为递推提供起始条件,需设定:

  • dp[0] = 1,表示空串有 1 种解码方式(基础情况)。

  • dp[1] 取决于第一个字符:若 s[0] != '0',则 dp[1] = 1,否则 dp[1] = 0

    若字符串以 '0' 开头,初始化已直接导致最终结果为 0,符合题意。

6.4 填表顺序(递推方向)

计算 dp[i] 依赖 dp[i-1]dp[i-2],下标均小于 i。因此必须从左到右 (即 i 从 2 到 n)依次填充 dp 表,确保每个状态计算时,其所有前置状态均已就绪

6.5 返回值(目标映射)

题目要求返回整个字符串 s 的解码方法总数 ,即长度为 n 的前缀,对应 dp[n] 。因此直接返回 dp[n]。若过程中任何位置无法解码,转移方程会自动将对应项置零,最终返回 0,满足题目要求。

七、代码

cpp 复制代码
class Solution {
public:
    int numDecodings(string s)
    {
        int n = s.size();
        vector<int> dp(n);//创建dp表

        //初始化
        if (s[0] != '0')
            dp[0] = 1;
        if (n == 1) return dp[0];//边界

        if (s[0] != '0' && s[1] != '0') dp[1] += 1;
        int t = (s[0] - '0') * 10 + s[1] - '0';
        if (t >= 10 && t <= 26)
            dp[1] += 1;

        for (int i = 2; i < n; i++)
        {
            //状态转移方程dp[i]=dp[i-1]+dp[i-2]
            if (s[i] != '0') dp[i] += dp[i - 1];
            int t = (s[i - 1] - '0') * 10 + s[i] - '0';
            if (t >= 10 && t <= 26)
                dp[i] += dp[i - 2];
        }
        return dp[n - 1];
    }
};

八、流程图

九、优化代码

cpp 复制代码
class Solution {
public:
    int numDecodings(string s) {
        int n = s.size();
        
        // 1. 创建dp表
        // dp[i] 表示字符串 s 的前 i 个字符(即 s[0..i-1])的解码方法总数
        vector<int> dp(n + 1, 0);
        
        // 2. 初始化
        // dp[0] = 1:空串可以视为一种解码方式,作为后续累加的基础。
        // 为什么给1?当处理两位数字时,若 s[1] 和 s[0] 构成合法两位数,
        // 则 dp[2] 需要加上 dp[0],此时 dp[0]=1 代表"两位数字作为一个整体"这一种情况。
        dp[0] = 1;
        
        // 处理第一个字符(索引0),若不为 '0',则单独解码成一种方案
        if (s[0] != '0')
            dp[1] = 1;
        
        // 如果字符串只有一个字符,直接返回结果(避免后续越界)
        if (n == 1) return dp[1];
        
        // 3. 填表顺序:从左到右(i 从 2 到 n)
        for (int i = 2; i <= n; i++) {
            // 情况一:当前字符 s[i-1] 单独解码(必须不为 '0')
            if (s[i - 1] != '0')
                dp[i] += dp[i - 1];
            
            // 情况二:当前字符与前一个字符 s[i-2] 组成两位数
            int twoDigit = (s[i - 2] - '0') * 10 + (s[i - 1] - '0');
            // 合法范围:10 ~ 26(注意 '01' 等前导零不合法,所以 t >= 10 已排除)
            if (twoDigit >= 10 && twoDigit <= 26)
                dp[i] += dp[i - 2];
        }
        
        // 4. 返回值:dp[n] 即为整个字符串的解码方法数
        return dp[n];
    }
};
相关推荐
Jerry2 小时前
LeetCode 707. 设计链表
算法
疋瓞2 小时前
python和C++对比(1)_数据类型和数据结构
数据结构·c++·python
C语言小火车3 小时前
C++ 堆排序深度精讲:基于完全二叉树的选择排序进化,最坏情况 O(n log n) 的稳定王者
开发语言·c++·算法·排序算法·堆排序
kebidaixu3 小时前
两轮BMS AFE SH367306 I2C 读写时序
算法
智能排队系统_头部供应商3 小时前
RK3588边缘网关改造银行排队系统实战
算法
Fabarta3 小时前
从 0 实现 ChatGPT 风格的流式对话 UI
算法·架构
手写码匠3 小时前
手写 AI 上下文压缩系统:从零实现 Prompt 压缩与选择性上下文管理
人工智能·深度学习·算法·aigc
ALex_zry4 小时前
C++26 std::complex 结构化绑定详解:auto [re, im] = c
c语言·开发语言·c++
EAI-Robotics4 小时前
机器人操作鲁棒性:当机械手遇上真实世界的不确定性
人工智能·算法·机器人