题目描述
给你一个整数数组 nums 和一个整数 k ,选择数组中的某个下标,将该下标对应的数字取反(正数变负数,负数变正数),总共可以进行 k 次取反操作。每次取反可以重复选择同一个下标。 请返回取反 k 次之后,数组能够得到的最大和。
核心贪心思想:想让总和尽可能大,优先把绝对值大的负数变成正数;当不存在负数可以翻转时,如果剩余翻转次数是奇数,只需要翻转全局绝对值最小的数字一次;偶数的话翻转同一个数字,正负抵消,总和不变。
算法思路
- 使用**小根堆(priority_queue + greater<int>)**保存全部数组元素。小根堆堆顶永远是当前最小的数字。
- 只要还有翻转次数
k>0,并且堆顶元素是负数:取出堆顶负数,取反后重新放回堆,消耗一次 k。- 这个操作等价于:优先把绝对值最大的负数转为正数。
- 当堆顶不再是负数(全部负数已经翻转完毕),退出循环。
- 如果此时仍然剩余翻转次数
k>0:- 剩余次数为偶数:重复翻转同一个数字,数字不变,总和不变,不需要任何操作。
- 剩余次数为奇数 :只需要翻转当前堆中最小的数字一次,总和损失
2*min_val。
- 最后遍历堆,累加所有元素,返回总和。
cpp
class Solution {
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
priority_queue<int, vector<int>, greater<int>> heap;
for (auto e : nums) {
heap.push(e);
}
while (k > 0 && !heap.empty() && heap.top() < 0) {
int x = heap.top();
heap.pop();
heap.push(-x);
k--;
}
if (k > 0) {
int t = k % 2;
if (t == 0) {
;
} else {
int x = heap.top();
heap.pop();
heap.push(-x);
}
}
int ret = 0;
while (!heap.empty()) {
ret += heap.top();
heap.pop();
}
return ret;
}
};