746. 使用最小花费爬楼梯
题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/min-cost-climbing-stairs/
题目
数组的每个索引作为一个阶梯,第 i
个阶梯对应着一个非负数的体力花费值 cost[i]
(索引从0开始)。
每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。
您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。
示例 1:
输入: cost = [10, 15, 20]
输出: 15
解释: 最低花费是从cost[1]开始,然后走两步即可到阶梯顶,一共花费15。
示例 2:
输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出: 6
解释: 最低花费方式是从cost[0]开始,逐个经过那些1,跳过cost[3],一共花费6。
注意:
cost
的长度将会在[2, 1000]
。- 每一个
cost[i]
将会是一个Integer类型,范围为[0, 999]
。
解题思路
思路:动态规划
先审题,题目给定一个数组 c o s t cost cost,数组每个索引表示一个阶梯,第 i i i 个元素 c o s t [ i ] cost[i] cost[i] 表示需要花费的体力(索引从 0 开始)。
先看这句话【每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。】,是个人觉得比较有歧义的地方。下面我说一下个人的理解。
这里爬上一个阶梯需要花费对应的体力值,其中这里对应的体力值,笔者将其理解为所爬上的这个阶梯对应的体力花费值。举个例子:
阶梯: 0 1 2 3
所花体力值: 2 3 4 5
假设要爬上第 2 2 2 个阶梯,在这里,要么从第 0 0 0 阶梯爬两个阶梯,要么从第 1 1 1 个阶梯爬一个阶梯。
在这里,根据前面所理解的含义,在这里爬到第 2 2 2 阶梯花费的总体力值应该分为以下两种情况:
- 从第 0 0 0 阶梯爬两个阶梯,总花费体力值为:第 0 0 0 阶梯跟第 2 2 2 阶梯所花体力值之和。这里是 2 + 4 2+4 2+4。
- 从第 1 1 1 个阶梯爬一个阶梯,总花费体力值为:第 0 0 0 阶梯跟第 1 1 1 阶梯所花体力值之和。这里是 3 + 4 3+4 3+4。
笔者这个理解是指踏至当前阶梯时,就需要花费当前阶梯对应的体力值。
那么现在的问题就是要求到楼顶的最小花费体力是多少,我们用动态规划的思路来解决。
状态定义
设 d p [ i ] dp[i] dp[i] 表示到达第 i i i 个阶梯最小花费体力值。
状态转移
题目已经说明
c
o
s
t
cost
cost 的长度是在区间
[
2
,
1000
]
[2, 1000]
[2,1000],那么到达第
i
i
i 个阶梯,要么是从
i
−
1
i-1
i−1 阶梯爬一个阶梯,要么是从
i
−
2
i-2
i−2 阶梯爬两个阶梯,求两种方案中的较小值。这里根据前面所解释的,按照踏至阶梯就应该加上当前阶梯对应的需花费体力值,对应的转移方程如下:
d
p
[
i
]
=
m
i
n
(
d
p
[
i
−
1
]
+
c
o
s
t
[
i
]
,
d
p
[
i
−
2
]
+
c
o
s
t
[
i
]
)
dp[i] = min(dp[i-1]+cost[i], dp[i-2]+cost[i])
dp[i]=min(dp[i−1]+cost[i],dp[i−2]+cost[i])
即是:
d
p
[
i
]
=
m
i
n
(
d
p
[
i
−
1
]
,
d
p
[
i
−
2
]
)
+
c
o
s
t
[
i
]
dp[i]=min(dp[i-1], dp[i-2]) + cost[i]
dp[i]=min(dp[i−1],dp[i−2])+cost[i]
状态初始化
题目中说明,开始可以选择从索引为 0 0 0 或者 1 1 1 的元素作为初始阶梯,按照踏至阶梯就应该加上当前阶梯对应的需花费体力值。那么此时:
- d p [ 0 ] = c o s t [ 0 ] dp[0] = cost[0] dp[0]=cost[0];
- d p [ 1 ] = c o s t [ 1 ] dp[1] = cost[1] dp[1]=cost[1]。
这个思路中,到达楼顶时也应该计算楼层顶部对应的花费体力值。但根据示例,顶层其实是没有列出花费的体力值。假设 c o s t cost cost 数组的长度为 n n n 时,索引对应的是第 0 0 0 阶梯到第 n − 1 n-1 n−1 个阶梯,其中第 n n n 个阶梯表示的顶部,由于顶层没有列出体力值,根据转移方程,最终转而求 d p [ n − 1 ] dp[n-1] dp[n−1] 和 d p [ n − 2 ] dp[n-2] dp[n−2] 两者的最小值即可。
具体的代码实现如下。
class Solution:
def minCostClimbingStairs(self, cost: List[int]) -> int:
n = len(cost)
# dp 数组
dp = [0] * n
# 初始化
dp[0] = cost[0]
dp[1] = cost[1]
# 遍历填充
for i in range(2, n):
dp[i] = min(dp[i-1], dp[i-2]) + cost[i]
# 返回 n-2 和 n-1 两者对应的较小值
return min(dp[n-1], dp[n-2])
这里也提一下在官解中提供的思路,其实是指踏出当前阶梯时,才需要将当前阶梯对应的体力值加上。还是沿用前面的例子:
阶梯: 0 1 2 3
所花体力值: 2 3 4 5
按照官方题解提供的思路,假如现在要从第 2 2 2 阶梯爬到第 3 3 3 阶梯时,求花费的总体力值?
其中这里设定是由第 0 0 0 阶梯爬至第 2 2 2 阶梯的,那么此时爬至第 2 2 2 阶梯花费的体力应该是从第 0 0 0 阶梯踏出,计算对应的体力值 2 2 2。第 2 2 2 阶梯到第 3 3 3 阶梯,这里从第 2 2 2 阶梯踏出,总体力值应该是前面的体力值 2 2 2 加上第 2 2 2 阶梯对应的体力值 4 4 4,这里是 2 + 4 2+4 2+4。
这就是跟笔者所述不同的地方,这里是仅在踏出阶梯的时候才计算体力值。
具体的思路可以查看下官方题解。
欢迎关注
公众号 【书所集录】
如有错误,烦请指出,欢迎指点交流。如果觉得写得还可以,麻烦点个赞,谢谢。