算法打卡day29

今日任务:

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

2)134.加油站

3)135.分发糖果

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

题目链接:1005. K 次取反后最大化的数组和 - 力扣(LeetCode)

给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)
以这种方式修改数组后,返回数组可能的最大和。

示例 1:
输入:A = [4,2,3], K = 1
输出:5
解释:选择索引 (1) ,然后 A 变为 [4,-2,3]。

示例 2:
输入:A = [3,-1,0,2], K = 3
输出:6
解释:选择索引 (1, 2, 2) ,然后 A 变为 [3,1,0,2]。

示例 3:
输入:A = [2,-3,-1,5,-4], K = 2
输出:13
解释:选择索引 (1, 4) ,然后 A 变为 [2,3,-1,5,4]。

提示:
1 <= A.length <= 10000
1 <= K <= 10000
-100 <= A[i] <= 100

文章讲解:代码随想录 (programmercarl.com)

视频讲解:贪心算法,这不就是常识?还能叫贪心?LeetCode:1005.K次取反后最大化的数组和哔哩哔哩bilibili

思路:

这题比较简单。

如果数组中有负数,且k!=0,那我们只需要将最小的负数反转就行了。

如果数组中没有负数,且k还是不为0,那我们只需要不停将最小值反转即可,这里可以先判断k,如果为偶数,不断反转后还是原来数字不变。如果k为奇数,最后为原来数字的相反数。

class Solution:
    def largestSumAfterKNegations(self, A: List[int], K: int) -> int:
        A.sort(key=lambda x: abs(x), reverse=True)  # 第一步:按照绝对值降序排序数组A

        for i in range(len(A)):  # 第二步:执行K次取反操作
            if A[i] < 0 and K > 0:
                A[i] *= -1
                K -= 1

        if K % 2 == 1:  # 第三步:如果K还有剩余次数,将绝对值最小的元素取反
            A[-1] *= -1

        result = sum(A)  # 第四步:计算数组A的元素和
        return result

感想:

正常思路我们先排序,按顺序反转即可。但是代码中,我们是按绝对值大小排序的,为什么要按绝对值的大小排序呢?这里有个技巧,因为这样遍历能保证,数组全为正数后,依旧是有序的,我们很容易取到最小值。当然如果数组还没有全为正,k已经没了,那也就不存在找最小值

134.加油站

题目链接:134. 加油站 - 力扣(LeetCode)

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

如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

说明:
如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。

示例 1:
输入:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。

示例 2:
输入:
gas = [2,3,4]
cost = [3,4,3]
输出: -1
解释: 你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油。开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油。开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油。你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。因此,无论怎样,你都不可能绕环路行驶一周。

文章讲解:代码随想录 (programmercarl.com)

视频讲解:贪心算法,得这么加油才能跑完全程!LeetCode :134.加油站哔哩哔哩bilibili

思路:

  • 首先,我们需要确保整个环路是可行的,也就是说,总加油量必须大于等于总消耗量。
  • 如果总加油量小于总消耗量,那么无论从哪个加油站出发,都不可能绕行一周。
  • 如果总加油量大于等于总消耗量,那么一定存在某个加油站,从该加油站出发可以绕行一周。
  • 我们可以通过模拟的方式,从每个加油站出发进行测试,看是否能够绕行一周。
  • 在模拟过程中,我们需要维护当前油箱中的汽油量,如果油箱中汽油不足以到达下一个加油站,则当前加油站不能作为出发点。
  • 当前加油站作为出发点的条件是,从当前加油站出发到下一个加油站时,油箱中汽油量必须大于等于0。

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        total_gas = sum(gas)  # 计算总加油量
        total_cost = sum(cost)  # 计算总消耗量
        
        # 如果总加油量小于总消耗量,则无法绕行一周,返回 -1
        if total_gas < total_cost:
            return -1
        
        n = len(gas)
        start = 0  # 初始出发点
        tank = 0  # 当前油箱中的汽油量
        
        # 从每个加油站开始模拟测试
        for i in range(n):
            tank += gas[i] - cost[i]  # 更新油箱中的汽油量
            # 如果油箱中汽油量小于0,说明无法到达下一个加油站,当前加油站不能作为出发点
            if tank < 0:
                start = i + 1  # 更新出发点为下一个加油站
                tank = 0  # 重置油箱中的汽油量
        
        return start  # 返回出发点

 这段代码首先计算了总加油量和总消耗量,如果总加油量小于总消耗量,则无法绕行一周,直接返回 -1。接下来,使用一个循环从每个加油站开始模拟测试,检查是否能够绕行一周。在模拟过程中,维护一个当前油箱中的汽油量变量,如果汽油量小于0,则更新出发点并重置油箱中的汽油量。

虽然,逻辑上我们应该先判断总加油量与总消耗量,符合要求再去比较,不过这里sum函数也需要遍历一遍历数组,所以在代码中,我们可以将计算总消耗与总加油量放到一个for循环中

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        total_gas = 0
        total_cost = 0
        cur_gas = 0
        start = 0
        
        for i in range(len(gas)):
            total_gas += gas[i]
            total_cost += cost[i]
            cur_gas += gas[i] - cost[i]
            # 如果当前汽油量小于0,说明无法到达下一个加油站,更新起始加油站为下一个加油站,并重置当前汽油量
            if cur_gas < 0:
                start = i + 1
                cur_gas = 0
        
        # 如果总加油量小于总消耗量,则无法绕行一周,返回 -1
        if total_gas < total_cost:
            return -1
        
        return start

135.分发糖果

题目链接:135. 分发糖果 - 力扣(LeetCode)

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?

示例 1:
输入: [1,0,2]
输出: 5
解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。

示例 2:
输入: [1,2,2]
输出: 4
解释: 你可以分别给这三个孩子分发 1、2、1 颗糖果。第三个孩子只得到 1 颗糖果,这已满足上述两个条件。

文章讲解:代码随想录 (programmercarl.com)

视频讲解:贪心算法,两者兼顾很容易顾此失彼!LeetCode:135.分发糖果哔哩哔哩bilibili

思路:

初始化糖果分配:首先,我们为每个孩子都分配一颗糖果。

从左向右遍历分发糖果:我们从左向右遍历孩子,并检查相邻孩子的评分情况。如果当前孩子的评分比左边孩子高,则我们给当前孩子分配的糖果数量比左边孩子多1个。

从右向左遍历修正糖果数量:在上一步中,我们确保了相邻孩子中评分更高的孩子获得了更多的糖果。但是,可能存在一种情况,即某个孩子评分虽然比右边孩子高,但是分配的糖果数量不多于右边孩子。这种情况下,我们需要从右向左遍历修正糖果数量,确保每个孩子都至少获得了其右边孩子分配的糖果数量加1个。

很明显孩子4,5,6评分比右边高,但是糖果并没有多。所以我们要再次从右向左遍历更新糖果

计算总糖果数量:最后,我们将所有孩子分配的糖果数量相加,得到总的糖果数量。这个总数满足了题目要求,即老师至少需要准备的糖果数量。

这样的思路确保了每个孩子都至少分配到了一个糖果,并且满足了相邻孩子中评分更高的孩子获得更多糖果的要求,从而得到了正确的答案。

class Solution:
    def candy(self, ratings: List[int]) -> int:
        # 初始化糖果分配数组,每个孩子至少一个糖果
        candyVec = [1] * len(ratings)

        # 从前向后遍历,处理右侧比左侧评分高的情况
        for i in range(1, len(ratings)):
            if ratings[i] > ratings[i - 1]:
                candyVec[i] = candyVec[i - 1] + 1

        # 从后向前遍历,处理左侧比右侧评分高的情况
        for i in range(len(ratings) - 2, -1, -1):
            if ratings[i] > ratings[i + 1]:
                candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1)

        # 统计结果
        result = sum(candyVec)
        return result

感想:

这题不好想,不用代码实现也不好方法。直接理解思想然后实现代码即可

  • 35
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值