leetCode#746. Min Cost Climbing Stairs

Description

On a staircase, the i-th step has some non-negative cost cost[i] assigned (0 indexed).

Once you pay the cost, you can either climb one or two steps. You need to find minimum cost to reach the top of the floor, and you can either start from the step with index 0, or the step with index 1.

Example 1:

Input: cost = [10, 15, 20]
Output: 15
Explanation: Cheapest is start on cost[1], pay that cost and go to the top.

Example 2:

Input: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
Output: 6
Explanation: Cheapest is start on cost[0], and only step on 1s, skipping cost[3].

Note:

  1. cost will have a length in the range [2, 1000].
  2. Every cost[i] will be an integer in the range [0, 999].

Code

class Solution(object):
    def minCostClimbingStairs(self, cost):
        """
        :type cost: List[int]
        :rtype: int
        """
        if len(cost) == 1:
            return 0
        if len(cost) == 2:
            return min(cost[0], cost[1])
        return min(cost[0] + self.minCostClimbingStairs(cost[1:]), cost[1] + self.minCostClimbingStairs(cost[2:]))

常规的递归解法,注意当cost length为1的时候,需要返回0。因为根据我的测试,当在倒数第二级的时候也可以算已经到顶了,所以最后一级的cost可以当成0。
但是递归的解法肯定不是最优解,毕竟它是等同于将所有的情况遍历了一遍。实际去运行的时候print一下cost,可以看到有许多重复的输入。比如[1,2,3,4]这个序列,第一次递归后cost为[2,3,4]和[3,4],再一次递归后cost为[3,4]、[4]、[3],可以看到[3,4]就已经重复计算了一次了。这只是一个比较短的例子,当输入更长的时候,就会有更多的重复。因此我们需要将已经算过的数据存起来,这样再次用到的时候就不用再次计算了。

bottom-up

class Solution(object):
    def minCostClimbingStairs(self, cost):
        """
        :type cost: List[int]
        :rtype: int
        """
        dp = cost[0:2]
        for idx, i in enumerate(cost[2:]):
            dp.append(i + min(dp[idx + 1], dp[idx]))
        return min(dp[-1], dp[-2])

注意min(dp[idx + 1], dp[idx]),这里一开始写的是idx - 1, idx - 2结果答案怎么都不对,最后终于发现是自己的惯性思维导致的错误。由于for循环是从cost的第二位开始的,所以我就想当然的以为idx也是对应于原始cost,从第二位开始的。然而实际上idx表示的是cost[2:]这个新列表的索引,所以它还是从0开始的。
同样的思路,还有一种节省空间的方法:

author: candy.tgz
def minCostClimbingStairs(self, cost):
    n = len(cost)
    if n == 0 or n == 1:
        return 0
    min_cost0, min_cost1 = cost[0], cost[1]
    for i in range(2, n):
        min_cost0, min_cost1 = min_cost1, min(min_cost0, min_cost1) + cost[i]

    return min(min_cost0, min_cost1)

up-bottom

class Solution(object):
    def minCostClimbingStairs(self, cost):
        """
        :type cost: List[int]
        :rtype: int
        """
        dp = {}
        def go(cost, n):
            if n in dp :
                return dp[n]
            if n <= 1 :
                dp[n] = cost[n]
                return dp[n]
            dp[n] = (0 if n == len(cost) else cost[n]) + min(go(cost, n - 2), go(cost, n - 1))
            return dp[n]
        return go(cost, len(cost))

这个是从顶向下的做法,先递归到最低,然后由于底层的值已经算出来了,所以返回的时候不会重复计算。这里的dp用的是字典,因为用list的话,由于向dp[n]赋值时,其还不存在,所以无法赋值,但用字典的话就不会用这样的问题。

Conclusion

关于动态规划的问题,虽然最后的算法很简单,但是对于目前的我来说却不好写出。总结下方法吧,先列出个公式,然后再代码实现,这样逻辑上会清楚很多,不然一上来就想着怎么把状态保存下来也是蛮头疼。

Let ``dp[i]`` be the minimum cost to reach the i-th stair.

Base cases:

``dp[0]=cost[0]``
``dp[1]=cost[1]``

DP formula:

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

比如写出这样的公式后,再去写代码就会轻松许多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值