LC1005-次取反后最大化的数组和

135 阅读1分钟

题目名称:K次取反后最大化的数组和

给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组:

  • 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。

重复这个过程恰好 k 次。可以多次选择同一个下标 i 。

以这种方式修改数组后,返回数组 可能的最大和 。

 

示例 1:

输入: nums = [4,2,3], k = 1
输出: 5
解释: 选择下标 1 ,nums 变为 [4,-2,3]

示例 2:

输入: nums = [3,-1,0,2], k = 3
输出: 6
解释: 选择下标 (1, 2, 2) ,nums 变为 [3,1,0,2]

示例 3:

输入: nums = [2,-3,-1,5,-4], k = 2
输出: 13
解释: 选择下标 (1, 4) ,nums 变为 [2,3,-1,5,4]

提示:

  • 1<=nums.length<=1041 <= nums.length <= 10^4
  • -100 <= nums[i] <= 100
  • 1<=k<=1041 <= k <= 10^4

思路分析

看见这道题说可以对一个值一直取反,是一个关键, 先把k次机会用在把数组中所有的负数取反, 如果k次机会没有用完,就判断k是否是二的倍数 如果是偶数 让一直对最小的数一直取反,会是正数 如果不是 最小的正数就会变成负数

因为题目要求是求最大和,所以每次变化相反数,只要选择数组中最小的元素即可~
明白这个原理后,最容易想到的就是利用最小堆,每次变换后要求重新计算最小值,这时候优先队列刚号能够满足需求

每次取出最小数字取负,取K此既可。 k次数可以优化,k和数组中负数数量对比,小于等于的时候k次全部使用,当所有数组不存在负数时候,剩余的次数若是偶数则全都对一个数使用则不变,若是奇数只反转最小数既可。

Code实现

public int largestSumAfterKNegations(int[] nums, int k) {
    PriorityQueue < Integer > pq = new PriorityQueue < > ();
    int ans = 0;
    int minusNum = 0;
    for (int num: nums) {
        if (num < 0) {
            minusNum++;
        }
        pq.offer(num);
        ans += num;
    }
    if (k > minusNum) {
        k = ((k - minusNum) & 1) == 1 ? 1 + minusNum : minusNum;
    }
    while (k > 0) {
        int temp = pq.poll();
        ans -= temp;
        temp = -temp;
        pq.offer(temp);
        ans += temp;
        k--;
    }
    return ans;
}

结果

Snipaste_2023-05-02_23-41-00.png

算法复杂度分析

  • 时间复杂度:O(n)O(n)
  • 空间复杂度:O(n)O(n)