难度简单
给你一个整数数组 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 <= 104
-100 <= nums[i] <= 100
1 <= k <= 104
思路一
这个是自己想出来的,不愧是自己做出来的根本不知道自己用的是贪心.
我们先来理解一下题意:
他说给你一个数组,你可以修改任意下标的元素,并且可以多次修改,修改k次(nums[i] = -nums[i]),让你返回最大和.
我第一个想法就是,每次修改最小的,然后更新一下,在修改最小的在更新一下.......直到修改k次为止,然后将其数组累加和返回.
那么肯定不能一直更新数组,那我就想到了优先级队列这种数据结构,默认是小根堆,每次弹出的就是最小的元素,然后自动帮我们调整堆.
思路总结
- 将数组中的所有元素全部放到堆中,他会自动帮你调整堆
- 然后每次弹出最小的元素,将其修改(nums[i]=-nums[i])
- 最后将堆中所有元素的累加和返回,就是修改k次的最大累加和
class Solution {
public int largestSumAfterKNegations(int[] nums, int k) {
PriorityQueue<Integer> queue = new PriorityQueue<>();
for(int i =0;i<nums.length;++i) {
queue.offer(nums[i]);//将数组元素加入到优先级队列中
}
for(int i =0;i<k;++i) {//每次将修改数组中元素最小的那个
int val = queue.poll();
queue.offer(-val);
}
//修改完之后,将优先级队列中的元素弹出累加之和返回
int sum = 0;
while(!queue.isEmpty()) {
sum += queue.poll();
}
return sum;
}
}
思路二
这个是参考网上大佬写的(英文力扣题解访问次数最高的那个人).
它的思路是这样的,先将数组进行排序(升序排序sort就行).元素大小肯定是从小到大,如果是负的就修改为正的,这样确保负的全变为正的使得整个数组和最大.
然后如果还剩下剩余的修改次数k,那么修改最小的那个元素才会使得整个数组之和最大,如果剩余修改次数是偶数,就不会改变数组和(因为修改两次最小的那个元素不会改变). 如果剩余修改次数是奇数,也是只有修改最小的那个元素才能使得整个数组之和最大,所以需要减去两次最小值min
为什么要减去两次最小值??
比如数组总和为8,数组最小值为2,而这时修改次数为奇数,证明我当是加的应该是-2(6-2)而不是2(6+2).
所以还要将2减去2次(可以理解为减去多加的2,再加上应该加的-2).
根据以上思路,我们肯定要记录最小值
核心思想就是保证数组之和最大,然后修改数组中的最小值,保证最终数组之和最大
也是贪心思想:
保证每次都修改负数为正 达到局部最优
整个数组之和最大,最后如果还剩余修改次数,就修改数组中最小的元素达到全局最优.
思路总结
- 先将数组进行排序->从小到大.升序
- 遍历数组元素,进行k次修改,如果元素为负数,将其修改,保证数组之和最大.利用sum累加数组元素
- 同时记录数组中最小值(全变为正的最小值),以便后续剩余修改次数,方便对其最小值再次进行修改使其数组之和最大达到全局最优.
- 最后如果没有剩余次数直接返回sum,如果剩余次数为偶数,无论怎么修改最小值都不变,也是直接返回sum,如果是奇数,返回sum-2*min 修改最小值使其数组总和最大
class Solution {
public int largestSumAfterKNegations(int[] nums, int k) {
Arrays.sort(nums);//升序排序
int min = Integer.MAX_VALUE;//记录最小值
int sum = 0;//路累加和
for(int i =0;i<nums.length;++i) {
if(nums[i]<0&&k>0) {//如果是负的就进行修改,同时修改次数--,保证数组之和最大
nums[i] = -nums[i];
k--;
}
sum += nums[i];//累加数组元素
min = Math.min(nums[i],min);//记录最小值
}
//return k<=0 ? sum : sum - (k%2)*2*min;
return k<=0 ? sum : (k%2==0) ? sum : sum - 2*min;//这样更容易理解
}
}