代码随想录 Day 34 | 【第八章 贪心算法 part 03】134. 加油站、135. 分发糖果、860.柠檬水找零、406.根据身高重建队列

一、134. 加油站

本题有点难度,不太好想,推荐大家熟悉一下方法二

代码实现

(1)定义变量current_sum用于统计每一个站点的剩余油量;变量total_sum用于统计整个的剩余油量,如果total_sum小于0,说明无论从哪里出发都无法跑完一圈; 思路是只要current_sum小于0,那么就从i+1出发,所以定义一个变量start用于记录新的起始位置。

(2)for循环遍历数组:current_sum累加每一个站点剩余的油量,也就是补充油量减去消耗油量;同时total_sum也和current_sum一样去进行统计;如果current_sum小于0,说明i之前都不适合作为起始位置,所以start更新为i+1,并且current_sum归0。

(3)for循环结束后,total_sum小于0,那么返回-1;否则就返回start。

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        start = 0
        cur_sum = 0
        total_sum = 0
        for i in range(len(gas)):
            cur_sum += (gas[i]-cost[i])
            total_sum += (gas[i]-cost[i])
            if cur_sum < 0:
                start = i+1
                cur_sum = 0
        if total_sum < 0:
            return -1
        return start

 

二、135. 分发糖果

本题涉及到一个思想,就是想处理好一边再处理另一边,不要两边想着一起兼顾,后面还会有题目用到这个思路

 1. 解题思路

        当既要比较左边又要比较右边的情况下,不能两边同时比较,因为会混淆,所以我们采用的方法是两边分开进行比较。

(1)只看右边小孩比左边小孩得分高的情况:在代码上表现为当右边孩子比左边孩子高,也就是rating[ i ] > rating[ i-1 ],那么右边孩子的糖果数就是左边孩子糖果数加1,也就是candy[ i ]等于candy[ i - 1]+1。

(2)左边小孩比右边小孩得分高的情况:此时需要从后向前遍历,因为这样才可以用到(1)的结果,否则比较会失效。如果左边小孩比右边小孩得分高的话,即rating[ i ]大于rating[ i+1 ],也就是candy[ i ]更新为要candy[ i + 1] +1,但是由于(1)已经比较过右孩子比左孩子高的情况了,那么candy[ i ]应该更新为 二者较大的那个数,也就是如果 i 比左右孩子都大,那么它应该取较大的那个值才能保证比较是正确的。

2. 代码实现

(1)定义candy数组,并且默认每个初始值均为1,也就是每个小孩至少得到1个糖果。

(2)首先比较右小孩比左小孩得分高的情况:for循环rating数组,注意 i 从1开始,因为 i 需要和 i-1 进行比较。当右边孩子比左边孩子高,也就是rating[ i ] > rating[ i-1 ],那么右边孩子的糖果数就是左边孩子糖果数加1,也就是candy[ i ]等于candy[ i - 1]+1。

(3)其次,倒序遍历,在(2)的基础上比较左边小孩比右边小孩得分高的情况:由于倒序遍历,所以 i 从len( rating )-2开始,因为需要比较 i 和 i+1。如果左边小孩比右边小孩得分高的话,即rating[ i ]大于rating[ i+1 ],也就是candy[ i ]更新为要candy[ i + 1] +1,但是由于(1)已经比较过右孩子比左孩子高的情况了,那么candy[ i ]应该更新为 二者较大的那个数。

(4)遍历整个更新后的candy数组,定义result变量进行累加,最终返回结果。

class Solution:
    def candy(self, ratings: List[int]) -> int:
        result = 0
        candy = [1] * len(ratings)
        for i in range(1, len(ratings)):
            if ratings[i] > ratings[i-1]:
                candy[i] = candy[i-1]+1
        for i in range(len(ratings)-2, -1, -1):
            if ratings[i] > ratings[i+1]:
                candy[i] = max(candy[i], candy[i+1]+1)
        for i in range(len(candy)):
            result += candy[i]
        return result
                

三、860.柠檬水找零

本题看上好像挺难,其实很简单,大家先尝试自己做一做。

1. 解题思路

        可以发现,面额为5,无需找零;面额为10,找零只能是5;面额为20找零有两种策略,一是10加5,二是3张5。所以按照贪心策略,我们尽量保留较多的5,找零更加有优势,所以需要先消耗10。

2. 代码实现

(1)定义3个变量five、ten、twenty均初始化为0,由于记录目前手里对应面额钞票的数量。

(2)从头到尾遍历账单数组:

        情况1:遇到账单为5,那么five直接加1;

        情况2:如果收到账单为10,那么要对其进行找零:判断手上是否有5,如果没有5,就说明无法进行找零,只能return False,如果手上有5,那么可以对其找零,那么ten加1,five减1;

        情况3:如果收到20,那么需要优先选择10和5找零,如果ten和five都大于0,说明可以进行找零,那么ten和five均自减1,twenty则加1。如果策略1(10+5)不满足,就使用策略2(3张5元)进行找零:five-3,twenty+1。如果两个策略都不行,只能return False。

(3)循环完全结束,那么return True。

class Solution:
    def lemonadeChange(self, bills: List[int]) -> bool:
        five = 0
        ten = 0
        twenty = 0

        for i in range(len(bills)):
            if bills[i] == 5:
                five += 1
     
            if bills[i] == 10:
                if five > 0:
                    five -= 1
                    ten += 1
                else:
                    return False

            if bills[i] == 20:
                if five > 0 and ten > 0:
                    five -= 1
                    ten -= 1
                    twenty += 1
                elif five >= 3:
                    five -= 3
                    twenty += 1
                else:
                    return False
        return True

四、406.根据身高重建队列

本题有点难度,和分发糖果类似,不要两头兼顾,处理好一边再处理另一边。

1. 解题思路

        首先按照身高进行从高到低排序,然后再比较k,按k从小到大排序,如果k小于前面的k,那么直接将这个元素向前插入即可,不会影响前面身高的k。因为如果你一开始就比前面的身高要高的话,那么一开始排序就会排在前面,然而并不是,所以向前插入时也不会影响前面身高对应的k。

2. 代码实现

class Solution:
    def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
        people.sort(key = lambda x:(-x[0],x[1]))
        p = []
        for i in people:
            p.insert(i[1], i)
        return p

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值