AI 刷题 341. 修改数组求最大和问题 题解 | 豆包MarsCode AI刷题

37 阅读6分钟

341.修改数组求最大和问题

问题描述

小U有一个整数数组 nums 和一个整数 k。他可以执行以下操作恰好 k 次:选择某个下标 i 并将 nums[i] 替换为 -nums[i]。同一个下标 i 可以被多次选择进行操作。

小U想知道,通过这种方式修改数组后,能够得到的数组可能的最大和是多少。

思路解析

本题的核心目标是在给定可以对数组元素进行 k 次取反操作的条件下,找出经过这些操作后数组元素总和的最大值。整体思路是通过合理安排取反操作,优先处理负数元素,将其变为正数,若还有剩余操作次数,再根据剩余次数的奇偶性来进一步决定如何操作以达到总和最大。

  1. 利用排序简化操作顺序判断
    首先对数组进行排序(nums.sort()),这样数组中的元素就按照从小到大的顺序排列了。排序后可以方便地从最小的元素开始考虑取反操作,因为通常优先将负数取反能增加数组总和,而排序后的负数会集中在数组的前面部分。

  2. 处理负数元素取反
    通过循环遍历排序后的数组(for i in range(len(nums))),当遇到负数元素(nums[i] < 0)并且还有剩余操作次数(k > 0)时,就将该负数取反(nums[i] = -nums[i]),同时操作次数 k 减 1。这个过程就是尽可能地先把负数变成正数,因为正数对总和的贡献是正向的,这样能使数组总和不断增大。

  3. 处理剩余操作次数
    当完成对负数元素的取反操作后,如果还有剩余操作次数(k > 0),需要进一步考虑如何利用这些剩余次数来最大化数组总和。

    • 剩余次数为奇数的情况
      如果剩余操作次数 k 除以 2 的余数为 1k % 2 == 1),说明还能进行一次取反操作。此时再次对数组进行排序(因为之前的操作可能改变了数组顺序,重新排序确保找到最小的元素),然后将数组中最小的元素(此时最小元素应该是正数或者 0)取反(nums[0] = -nums[0])。这样做的原因是,取反一个较小的正数,相比于不取反,有可能使总和变得更大,因为取反后它变为负数,其绝对值会从总和中减去,而选择最小的正数进行取反能让这种 “减去” 的影响最小,从而在只剩余奇数次操作时尽量保证总和最大。
    • 剩余次数为偶数的情况
      如果 k 除以 2 的余数为 0(即剩余次数为偶数),那么不需要再进行额外操作了,因为对任何元素进行偶数次取反操作,元素本身不会改变,所以此时数组总和已经达到当前情况下的最大值。
  4. 计算并返回最终总和
    最后,通过 sum(nums) 计算经过上述一系列操作后的数组所有元素的总和,并将这个总和作为结果返回,这个总和就是在给定 k 次操作条件下数组能达到的最大总和。

解题步骤

  1. 函数定义与初始化(隐含在函数参数中)

定义了一个名为 solution 的函数,它接受两个参数:nums 是一个整数列表,表示给定的数组;k 是一个整数,表示可以进行取反操作的次数。函数的返回值类型为整数,代表经过 k 次操作后数组能达到的最大总和。

  1. 对数组进行排序

使用 Python 列表的 sort 方法对输入的 nums 数组进行排序,使得数组元素按照从小到大的顺序排列,方便后续按顺序处理元素进行取反操作,简化了判断哪些元素先进行取反的逻辑。

  1. 处理负数元素取反

通过一个 for 循环遍历排序后的数组,循环变量 i 从 0 到数组长度减 1。在每次循环中,判断当前元素 nums[i] 是否小于 0(即是否为负数)并且还有剩余操作次数 kk > 0),如果满足这两个条件,就将该负数元素取反(通过 nums[i] = -nums[i]),同时将操作次数 k 减 1,表示已经使用了一次操作机会。这个循环会持续执行,直到遍历完整个数组或者操作次数 k 用完为止,目的是尽可能多地把负数变为正数来增加数组总和。

  1. 处理剩余操作次数(分奇偶情况)

当完成对负数元素的取反操作后,检查是否还有剩余操作次数(k > 0)。如果有剩余次数,再判断剩余次数 k 的奇偶性。如果 k 除以 2 的余数为 1(即剩余次数为奇数),那么需要再进行一次取反操作来最大化总和。首先再次对数组进行排序(因为前面的取反操作可能改变了数组元素的顺序,重新排序确保找到当前数组中最小的元素),然后将数组中的第一个元素(索引为 0,此时这个元素应该是正数或者 0)取反(nums[0] = -nums[0]),通过取反最小的正数来使总和在剩余奇数次操作的情况下达到最大。

复杂度分析

时间复杂度

代码里有对数组的排序操作,Python 中排序一般时间复杂度是 (n 是数组 nums 的长度),还有遍历数组的循环,时间复杂度为O(n)。不过主要时间消耗在排序上,最多排两次序,所以整体时间复杂度就是O(n log n)。

空间复杂度

函数内部除了接收的参数外,额外用的变量占空间很少,基本是常数级别。而输入的数组 nums 占空间随它的长度 n 变化,所以整体空间复杂度为O(n)。

Code

def solution(nums: list, k: int) -> int:
    # 1. 对数组进行排序
    nums.sort()
    
    # 2. 从最小的负数开始,依次取反
    for i in range(len(nums)):
        if nums[i] < 0 and k > 0:
            nums[i] = -nums[i]
            k -= 1
    
    # 3. 如果还有剩余操作次数
    if k > 0:
        # 如果剩余操作次数是奇数
        if k % 2 == 1:
            # 找到最小的正数并取反
            nums.sort()
            nums[0] = -nums[0]
    
    # 4. 返回数组的总和
    return sum(nums)

if __name__ == '__main__':
    print(solution(nums=[-4, 2, 3], k=1) == 9)
    print(solution(nums=[-3, -1, 0, 3], k=3) == 7)
    print(solution(nums=[-2, -3, -1, 5, -4], k=2) == 9)