代码随想录算法训练营第三十三天丨1005. K 次取反后最大化的数组和、134. 加油站、135. 分发糖果

文章介绍了两种贪心算法的应用:一种是通过排序和反转找到1005.K次取反后数组的最大和,另一种是解决加油站问题,判断是否能完成环形路线。还涉及了分发糖果问题,确保每个孩子获得公平的糖果。
摘要由CSDN通过智能技术生成

1005. K 次取反后最大化的数组和

两次贪心:

先按绝对值排序,第一次从大到小反转绝对值大的负值。如果数组都非负了k还有剩余,一直反转绝对值最小的值直到k为0。

class Solution:
    def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:
        nums.sort(key = abs, reverse = True)
        for i in range(len(nums)):
            if k == 0:
                break
            elif nums[i] < 0:
                nums[i] *= -1
                k -= 1
        print(nums)
        if k > 0:
            nums[-1]  = (-1) ** k * nums[-1]
        return sum(nums) 

134. 加油站

我的思路:

我的想法是,计算两个数组的差值数组,元素表示的就是从i 到 i + 1剩余的油量。由于是环形路线,将diff数组扩展一倍,如果存在非负最大子数组,最大子数组的起始点就是结果。

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        diffs = [gas[i] - cost[i] for i in range(len(gas))] * 2
        cur_max = global_max = diffs[0]
        i = 0
        for pos in range(1, len(diffs)):
            if cur_max < 0:
                i = pos
                cur_max = diffs[pos]
            else:
                cur_max += diffs[pos]
            global_max = max(cur_max, global_max)
        return i if i < len(gas) else -1

优化:

实际上没有想到这道题的精髓,这道题不需要求最大子数组,大小什么的都无所谓,只要能是非负的就可以了。

首先要明确一点,如果总油量大于总消耗,一定从某点开始可以环绕一周,算法就是按照证明这一点设计的。如果总油量小于总消耗不能绕一周很好理解,那如何证明总油量不小于总消耗,一定可以环绕一周呢。

情况一:如果从0开始一直到n - 1一直是非负差值和,根据总油量不少于总消耗,说明从0开始就能绕一周,不需证明。

情况二:从0开始到i,在i处发现差值和为负,这说明从0到i总油量小于总消耗,从0到i任意一点开始无法到达i+1,但是整个环形线路总油量是大于总消耗的,说明假如从i + 1起步,i + 1到 n - 1之间,总油量是严格大于总消耗的,并且差值至少是之前累计的所有的负值。因此,把起始点移动到i + 1,继续累计差值,每次发现差值和为负,继续寻找下一个作为起点,直到某次ik + 1到 n - 1的差值和总为正,这说明从ik + 1不仅能到n - 1,还能弥补之前所有的负值和到达ik,因为总油量不小于总消耗,最终从ik能够到达ik + 1,ik + 1就是起点。

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        start = curSum = totalSum = 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. 分发糖果

一次从左向右遍历,确保每个孩子比他左边评分高的孩子获得更多的糖果;另一次从右向左遍历,确保每个孩子比他右边评分高的孩子获得更多的糖果。通过这两次遍历,可以保证所有孩子的糖果数既满足左边的条件,也满足右边的条件。

class Solution:
    def candy(self, ratings: List[int]) -> int:
        n = len(ratings)
        candies = [1] * n

        # 从左向右确保评分高的孩子比他左边的孩子获得更多的糖果
        for i in range(1, n):
            if ratings[i] > ratings[i - 1]:
                candies[i] = candies[i - 1] + 1

        # 从右向左确保评分高的孩子比他右边的孩子获得更多的糖果
        for i in range(n - 2, -1, -1):
            if ratings[i] > ratings[i + 1]:
                candies[i] = max(candies[i], candies[i + 1] + 1)

        # 计算总糖果数
        return sum(candies)

好难想到的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值