"斐波那契,动态规划开始的地方"
开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第17天,点击查看活动详情
746. 使用最小花费爬楼梯 题目描述:给你一个整数数组 ,其中 是从楼梯第 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为 或下标为 的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。
输入:
输出:
解释: 你将从下标为 的台阶开始。支付 ,向上爬两个台阶,到达楼梯顶部。总花费为 。
输入:
输出:
解释: 你将从下标为 的台阶开始。
- 支付 ,向上爬两个台阶,到达下标为 的台阶。
- 支付 ,向上爬两个台阶,到达下标为 的台阶。
- 支付 ,向上爬两个台阶,到达下标为 的台阶。
- 支付 ,向上爬一个台阶,到达下标为 的台阶。
- 支付 ,向上爬两个台阶,到达下标为 的台阶。
- 支付 ,向上爬一个台阶,到达楼梯顶部。总花费为 。
中规中矩的动态规划
探寻最优子结构:如果我们知道到第 级台阶时的最小花费,记作 ,那么当 时,我们可以从 第 级台阶直接爬到第 级台阶,此时 ,我们也可从第 级台阶直接爬到第 级台阶,此时 。那么 选择两者最小值即可。
1、确定 dp 状态数组
定义 是到达第 级台阶时的最小开销,。台阶从 开始计数,当我们到达第 级台阶时,依然要支付 的费用才算到达顶点,所以我们对 数组延长一个"哨兵"节点。
2、确定 dp 状态方程
在探寻最优子结构时,已经得到转移方程,即
3、确定 dp 初始状态
根据题目条件 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。,那么到第 级或第 级台阶均没有开销,即 ;
4、确定遍历顺序
从 顺序遍历到 (拓展一个“哨兵”节点,所以 可以遍历到 )。
5、确定最终返回值
回归到状态定义中, 即为返回值。
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;
};