• 代码随想录学习Day 29


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

    题目链接

    讲解链接

    思路:先对数组进行排序,保证数组中最小的值(也就是取反后损失最小的值)位于数组最前端。然后对数组进行遍历,在k次内尽可能将负数全部取反。当数组中元素全部>=0或者k为0时结束循环。此时再对数组进行排序,保证小数在前(因为可能对负数取反后导致原本顺序改变)。然后判断当前的k值,如果为偶数,则直接返回数组和(因为偶数次取反没有影响);如果是奇数,就将数组中最小的数取反,保证影响最低。

    1. class Solution:
    2. def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:
    3. nums.sort() # 先排序,保证负数在前
    4. for i in range(len(nums)):
    5. if nums[i] < 0 and k != 0: # 尽可能多的将负数取反
    6. nums[i] = 0 - nums[i]
    7. k -= 1
    8. if all(item >= 0 for item in nums) or k == 0: # 数组中全部>=0或者k为0时结束循环
    9. break
    10. nums.sort() # 再次排序
    11. if k % 2 == 0: # k为偶数直接返回sum
    12. return sum(nums)
    13. else:
    14. nums[0] = 0 - nums[0] # 奇数则对最小的数取反
    15. return sum(nums)

    优化版本,根据绝对值进行排序,可以少进行一次数组排序的操作。

    1. class Solution:
    2. def largestSumAfterKNegations(self, A: List[int], K: int) -> int:
    3. A.sort(key=lambda x: abs(x), reverse=True) # 第一步:按照绝对值降序排序数组A
    4. for i in range(len(A)): # 第二步:执行K次取反操作
    5. if A[i] < 0 and K > 0:
    6. A[i] *= -1
    7. K -= 1
    8. if K % 2 == 1: # 第三步:如果K还有剩余次数,将绝对值最小的元素取反
    9. A[-1] *= -1
    10. result = sum(A) # 第四步:计算数组A的元素和
    11. return result

    134.加油站

    题目链接

    讲解链接

    暴力解法:

    1. class Solution:
    2. def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
    3. n = len(gas)
    4. for i in range(n): # 外层i确定起始位置
    5. cur_gas = gas[i] - cost[i] # 当前油箱中的油量
    6. index = (i + 1) % n
    7. while cur_gas > 0 and index != i: # 用while循环模拟从i出发行驶一圈的过程
    8. cur_gas += gas[index] - cost[index] # 当前油量变化
    9. index = (index + 1) % n # 移动到下一个地点,因为是环形所以要取模
    10. if cur_gas >= 0 and index == i: # 如果油量还有剩余或者刚好够,并且能够回到出发点
    11. return i # 返回出发点的下标
    12. return -1

    贪心法:可以换一个思路,首先如果总油量减去总消耗大于等于零那么一定可以跑完一圈,说明 各个站点的加油站 剩油量rest[i]相加一定是大于等于零的。每个加油站的剩余量rest[i]为gas[i] - cost[i]。i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置,因为这个区间选择任何一个位置作为起点,到i这里都会断油,那么起始位置从i+1算起,再从0计算curSum。

    1. class Solution:
    2. def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
    3. cur_sum, start, total = 0, 0, 0 # 当前累计剩余油量,起始位置,总剩余油量
    4. for i in range(len(gas)):
    5. cur_sum += gas[i] - cost[i]
    6. total += gas[i] - cost[i]
    7. if cur_sum < 0: # 若当前剩余油量小于0
    8. start = i + 1 # 起始位置更新为i+1
    9. cur_sum = 0 # 重新从0开始统计
    10. if total < 0:
    11. return -1 # 总剩余油量totalSum小于0,说明无法环绕一圈
    12. return start

    局部最优:当前累加rest[i]的和curSum一旦小于0,起始位置至少要是i+1,因为从i之前开始一定不行。

    全局最优:找到可以跑一圈的起始位置。 

    135.分发糖果

    题目链接

    讲解链接

    思路:先从前往后遍历,处理所有右孩子评分大于左孩子评分的情况;在从后往前遍历,处理左孩子评分大于右孩子评分的情况。

    局部最优:右边评分比左边大,右边多一个;左边评分比右边大,左边多一个。

    全局最优:相邻孩子中,评分高的右孩子糖果比左边孩子多;评分高的左孩子糖果比右边孩子多。

    当从后往前遍历时,如果遇到左孩子大于右孩子的情况,还需要考虑之前遍历过程中candy[i]的值,不能直接赋值为candy[i + 1] + 1,而是要取candy[i + 1] + 1 和 candy[i] 最大的糖果数量,candy[i]只有取最大的才能既保持对左边candy[i - 1]的糖果多,也比右边candy[i + 1]的糖果多

    1. class Solution:
    2. def candy(self, ratings: List[int]) -> int:
    3. candy = [1] * len(ratings) # 初始化candy数组全1,因为每个孩子都至少获得一个糖果
    4. for i in range(1, len(ratings)): # 从前往后遍历,考虑右孩子大于左孩子的情况
    5. if ratings[i] > ratings[i - 1]: # 如果右大于左
    6. candy[i] = candy[i - 1] + 1 # 右的糖果数为左+1
    7. for i in range(len(ratings) - 2, -1, -1): # 从后往前遍历,考虑左孩子大于右孩子的情况
    8. if ratings[i] > ratings[i + 1]: # 如果左大于右
    9. candy[i] = max(candy[i + 1] + 1, candy[i]) # 取两者最大值
    10. return sum(candy) # 返回糖果总和
  • 相关阅读:
    ky10-server docker 离线安装包、离线安装
    MyBatis-Plus中的更新操作(通过id更新和条件更新)
    20221207今天的世界发生了什么
    ADB 命令
    【博客460】BGP(边界网关协议)-----bgp与ospf等路由协议的区别
    探讨Linux CPU的上下文切换原由
    最新Java面试真题,备战金九银十。
    【恋上数据结构与算法】理论 二:动态数组
    网络编程与黏包问题
    电脑版WPS怎么将更新目录加到快速访问栏
  • 原文地址:https://blog.csdn.net/weixin_44449447/article/details/137816451