《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];
    }
};
相关推荐
m沐沐3 小时前
【深度学习】YOLOv2目标检测算法——改进点、网络结构与聚类先验框解析
人工智能·pytorch·深度学习·算法·yolo·目标检测·transformer
不会就选b3 小时前
算法日常・每日刷题--<链表>3
数据结构·算法·链表
geovindu3 小时前
go: Iterative Algorithms
开发语言·后端·算法·golang·迭代算法
Zachery Pole5 小时前
CCF-CSP备战NO.7【队列】
算法
闪电悠米6 小时前
力扣hot100-48.旋转图像-转置翻转详解
算法·leetcode·职场和发展
满天星83035776 小时前
【算法】最长递增子序列(三种解法)
算法
hold?fish:palm6 小时前
kv存储主从复制的设计与实现
c++·redis·后端
啦啦啦啦啦zzzz7 小时前
算法:贪心算法
c++·算法·leetcode·贪心算法
charlie1145141917 小时前
现代C++工程实践 WeakPtr 实战(三):WeakPtrFactory 与「最后成员」惯用法
开发语言·c++·开源项目·现代c++
清泓y8 小时前
RAG 技术
算法·ai