Day29 贪心算法part3

任务

134. 加油站

在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。

思路

暴力解法考虑循环列表的遍历,利用while和取余操作。
贪心法:只要之前累计的sum为负数,就从下一个开始重新累计,用start标记新开始的位置。另一个总的累计变量判断整体的利润(加油-损耗)是否大于等于0,退出循环后,根据整体利润判断返回-1还是返回start。
注意为什么不会在更新start之前的某个索引为起点的情况,因为我们是顺序遍历,之前如果遇到了所说的情况,在遍历到那个节点的时候一定已经更新start了。

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        curSum = 0
        totalSum = 0
        start = 0
        for i in range(len(gas)):
            curSum += gas[i] - cost[i]
            totalSum += gas[i] - cost[i]
            if curSum < 0: # 新起点重新累计
                start =  i+1
                curSum = 0
        if totalSum < 0: return -1
        return start

135. 分发糖果

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。
相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

思路

一次性遍历无法解决原因是,主要是在candies[i-1] > candies[i]这种情况,我们不知道后面有几个连续小(或等)的值,也就无法去给遍历到的节点赋值。因此使用将规则分为两个的做法。
从左到右,判断右边大的情况,然后从右到左,判断左边大的情况,注意第二次遍历时需要综合左右满足题意。说起来简单,实际上很难想到。但是提供了一种思路,即两边分开考虑。

class Solution:
    def candy(self, ratings: List[int]) -> int:
        candies = [1] * len(ratings)
        for i in range(1,len(ratings)):
            if ratings[i] > ratings[i-1]: # 从前往后,判断右大
                candies[i] = candies[i-1] +1
        
        for i in range(len(ratings)-2,-1,-1): #注意这里的写法与从前到后保持一致,即第一个i实际是倒数第二个,i+1是最后一个
            if ratings[i+1] < ratings[i]: # 从后往前,判断左大
                candies[i] = max(candies[i],candies[i+1]+1)
        
        sum = 0 
        for elem in candies:
            print(elem)
            sum += elem
        return sum

860. 柠檬水找零

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。

每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。

注意,一开始你手头没有任何零钱。

给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。

思路

遍历数组,根据每种情况分别去实际模拟,注意20块时,有找一个10块和一个5块,以及三个5块两种找法,优先第一种找法,因为5块更通用,除了能够满足给20块找零,还能满足给10块找零。

class Solution:
    def lemonadeChange(self, bills: List[int]) -> bool:
        five = 0
        ten = 0
        for bill in bills:
            if bill == 5:
                five += 1
            elif bill == 10:
                if five >= 1:
                    ten+=1
                    five-=1
                else: return False
            elif bill ==20:
                if ten >= 1 and five >=1:
                    ten-=1
                    five-=1
                elif five >= 3:
                    five-=3
                else: return False
        return True

406. 根据身高重建队列

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。

请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

思路

根据身高先排序(从大到小),相同情况再根据k排序(从小到大), 然后在结果列表中将它们添加到相应的位置。
这里隐含了按规则排好序后,插入的索引一定在合法范围内,比如已经落位了n个数,则插入的索引一定在[0,n]中

class Solution:
    def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
        people.sort(key= lambda x:(-x[0],x[1])) #先按照高度从大到小排,高度相同情况下按照k从小到大排
        
        result = []
        for person in people:
            result.insert(person[1],person)
        return result
posted @ 2024-08-14 13:13  haohaoscnblogs  阅读(23)  评论(0)    收藏  举报