746. 使用最小花费爬楼梯 (min cost climbing stairs)

3,904 阅读2分钟

"斐波那契,动态规划开始的地方"

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第17天,点击查看活动详情

746. 使用最小花费爬楼梯 题目描述:给你一个整数数组 costcost,其中 cost[i]cost[i] 是从楼梯第 ii 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为 00 或下标为 11 的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费

输入: cost=[10,15,20]cost = [10,15,20]

输出: 1515

解释: 你将从下标为 11 的台阶开始。支付 1515 ,向上爬两个台阶,到达楼梯顶部。总花费为 1515

输入: cost=[1,100,1,1,1,100,1,1,100,1]cost = [1,100,1,1,1,100,1,1,100,1]

输出: 66

解释: 你将从下标为 00 的台阶开始。

  • 支付 11 ,向上爬两个台阶,到达下标为 22 的台阶。
  • 支付 11 ,向上爬两个台阶,到达下标为 44 的台阶。
  • 支付 11 ,向上爬两个台阶,到达下标为 66 的台阶。
  • 支付 11 ,向上爬一个台阶,到达下标为 77 的台阶。
  • 支付 11 ,向上爬两个台阶,到达下标为 99 的台阶。
  • 支付 11 ,向上爬一个台阶,到达楼梯顶部。总花费为 66

中规中矩的动态规划

探寻最优子结构:如果我们知道到第 ii 级台阶时的最小花费,记作 dp[i]dp[i],那么当 i2i \ge 2 时,我们可以从 第 i2i - 2 级台阶直接爬到第 ii 级台阶,此时 dp[i]=dp[i2]+cost[i2]dp[i] = dp[i-2] + cost[i-2],我们也可从第 i1i - 1 级台阶直接爬到第 ii 级台阶,此时 dp[i]=dp[i1]+cost[i1]dp[i] = dp[i-1] + cost[i-1]。那么 dp[i]dp[i] 选择两者最小值即可。

1、确定 dp 状态数组

定义 dp[i]dp[i] 是到达第 ii 级台阶时的最小开销,i[0,n]i \in [0, n]。台阶从 00 开始计数,当我们到达第 n1n - 1 级台阶时,依然要支付 cost[n1]cost[n - 1] 的费用才算到达顶点,所以我们对 dpdp 数组延长一个"哨兵"节点。

2、确定 dp 状态方程

在探寻最优子结构时,已经得到转移方程,即

dp[i]=min(dp[i1]+cost[i1],dp[i2]+cost[i2])dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])

3、确定 dp 初始状态

根据题目条件 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。,那么到第 00 级或第 11 级台阶均没有开销,即 dp[0]=dp[1]=0dp[0] = dp[1] = 0;

4、确定遍历顺序

i=2i = 2 顺序遍历到 i=ni = n(拓展一个“哨兵”节点,所以 ii 可以遍历到 nn)。

5、确定最终返回值

回归到状态定义中,dp[n]dp[n] 即为返回值。

6、代码示例

/**
 * 空间复杂度 O(n),n是cost数组的长度
 * 时间复杂度 O(n)
 */
function minCostClimbingStairs(cost: number[]): number {
    const n = cost.length;
    const dp = new Array(n + 1).fill(0);
    
    for(let i = 2; i <= n; i++) {
        dp[i] = Math.min(dp[i - 1]+ cost[i - 1], dp[i - 2] + cost[i - 2]);
    }
    
    return dp[n]
};

状态压缩

/**
 * 空间复杂度 O(1)
 * 时间复杂度 O(n)
 */
function minCostClimbingStairs(cost: number[]): number {
    let prev = 0, curr = 0;
  
    for (let i = 2, n = cost.length; i <= n; i++) {
        const next = Math.min(curr + cost[i - 1], prev + cost[i - 2]);
        prev = curr;
        curr = next;
    }
   
    return curr;
};

参考

# 重识动态规划