【LeetCode贪心#05】K 次取反后最大化的数组和(自定义sort、二重贪心)
K次取反后最大化的数组和
给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)
以这种方式修改数组后,返回数组可能的最大和。
示例 1:
- 输入:A = [4,2,3], K = 1
- 输出:5
- 解释:选择索引 (1,) ,然后 A 变为 [4,-2,3]。
示例 2:
- 输入:A = [3,-1,0,2], K = 3
- 输出:6
- 解释:选择索引 (1, 2, 2) ,然后 A 变为 [3,1,0,2]。
示例 3:
- 输入:A = [2,-3,-1,5,-4], K = 2
- 输出:13
- 解释:选择索引 (1, 4) ,然后 A 变为 [2,3,-1,5,4]。
提示:
- 1 <= A.length <= 10000
- 1 <= K <= 10000
- -100 <= A[i] <= 100
思路
要用贪心的方式去想解决方法
一个直观的想法是:
局部最优:让绝对值大的负数变为正数
全局最优:让数组元素的和最大
要实现上面的想法,需要先将数组进行排序(注意,此处排序包括负数,所以得自定义排序规则)
排完序之后就可以从小到大将负数反转为正数
但如果出现以下情况:(数组已经排序)
{-1, -2, -3, 5, 8}, K = 4
---->{1, 2, 3, 5, 8}, K = 1
即将所有负数从小到大反转之后,K还有剩余
此时问题就变成了:在一个正整数数组中如何取反K次以获得最大数组元素和
那么这里又要制定新的贪心策略
局部最优:找到数组中数值最小的正整数进行反转(这样对整体影响最小)
全局最优:让数组元素的和最大
题外话:自定义sort排序规则
参考:https://www.cnblogs.com/Daniel-lmz/p/16452975.html
sort(iterator beg, iterator end, _Pred);
// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置
// beg 开始迭代器
// end 结束迭代器
// _Pred 谓词(即cmp,排序的方式)
对[first****,last)(一定要注意这里的区间是左闭又开)区间内数据根据cmp的方式进行排序。也可以不写第三个参数,此时按默认排序,从小到大进行排序。
例子:自定义sort的排序规则(从大到小),默认是从小到大
static bool cmp(int a,int b){
return b < a;
}
sort(a,a+n,cmp);
代码
步骤
依据上述思路可以总结出以下步骤:
1、先自定义排序规则,对含有负数的数组进行排序(自定义规则是:按照绝对值大小进行排序)
例子:{-3, -1, 0, 2}------->{-3, 2, -1, 0}
2、排好之后,遍历数组,从小到大将负数元素反转为正数,同时K--
3、如果负数已经被反转完,K仍有剩余,则不断反转当前数组中最小的正数,直到用完K
4、求出当前数组的和,返回
class Solution {
static bool cmp(int a, int b) {
return abs(a) > abs(b);
}
public:
int largestSumAfterKNegations(vector<int>& nums, int k) {
sort(nums.begin(), nums.end(), cmp);//对含有负数的数组进行排序
for(int i = 0; i < nums.size(); ++i){//遍历数组,从小到大将负数元素反转为正数
if(nums[i] < 0 && k > 0){//第一处贪心
nums[i] = -nums[i];
k--;
}
}//如果负数已经被反转完,K仍有剩余,则不断反转当前数组中最小的正数(就是数组最后一个数),直到用完K
if(k % 2 == 1){//第二处贪心
nums[nums.size() - 1] *= -1;//只在k为奇数时取反,因为偶数取反还是正数,没必要进行操作
}
//求数组和
int res = 0;
for(int num : nums) res += num;
return res;
}
};
注意点
1、自定义sort的规则
在写cmp函数时,记得要用static
修饰(参考本题题解)
2、反转亦有优化
当我们实现第二处贪心的逻辑时,需要对数组中的最小正数进行不断反转以消耗掉K值
此处是可以优化的
一个正数,翻转偶数次它还是正数
所以只需要在K为奇数的时候进行反转操作即可,这样可以省去偶数次反转操作带来的额外性能开支