给你一个整数数组 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] 。
贪心的思路,局部最优:让绝对值大的负数变为正数,当前数值达到最大,整体最优:整个数组和达到最大。
那么如果将负数都转变为正数了,K依然大于0,此时的问题是一个有序正整数序列,如何转变K次正负,让数组和达到最大,那么我们只要将最小的正整数取反即可,这样取到的和最大。
法一:
class Solution {
public int largestSumAfterKNegations(int[] nums, int k) {
/**
贪心思想:绝对值排序
*/
//先将数组根据绝对值从小到大进行排序
nums = IntStream.of(nums)
.boxed()
.sorted((o1, o2) -> Math.abs(o1) - Math.abs(o2))
.mapToInt(Integer::intValue).toArray();
//遍历数组 遇到负数且k>0 就将负数取反
//要从后向前进行取反 先将绝对值大的负数进行取反
for(int i=nums.length-1;i>=0;i--){
if(nums[i] < 0 && k > 0){
nums[i] = -nums[i];
k--;
}
}
//看k是否还剩余,如果是奇数,就将最小的正整数取反即可
if(k % 2 == 1){
nums[0] = -nums[0];
}
//数组求和
return Arrays.stream(nums).sum();
}
}
法二*(效率稍微高一些 代码复杂一些):
class Solution {
public int largestSumAfterKNegations(int[] nums, int k) {
Arrays.sort(nums);
int fuNums = 0;
int num_0 = -1;
int res = 0;
for(int i=0;i<nums.length;i++){
if(nums[i] < 0){
fuNums++;
}
if(nums[i] == 0){
num_0 = i; //找到0的下标位置
break;
}
}
int cnt = k - fuNums; //k与负数的差值
//System.out.println("fushu"+fuNums);
//负数大于等于k的情况下
if(fuNums >= k){
res = adds(nums,k);
}else if(fuNums > 0){
//负数小于k
if(num_0 >= 0 || (cnt%2==0)){
//存在0的情况
//先将所有的负数取反 其余的取反次数全部在零上作用
res = adds(nums,fuNums);
//System.out.println("26hang");
}else{
//没有零的情况
int a = -(nums[fuNums-1]);
if(fuNums<nums.length){
int b = nums[fuNums];
if(a >= b){
nums[fuNums] = -(nums[fuNums]);
}else{
nums[fuNums-1] = -(nums[fuNums-1]);
}
}else{
nums[fuNums-1] = -(nums[fuNums-1]);
}
res = adds(nums,fuNums);
}
}else{
if(k%2==0){
res = adds(nums,0);
}
else{
res = adds(nums,1);
}
}
return res;
}
public int adds(int num[],int fuNum){
int ress = 0;
for(int i=0;i<fuNum;i++){
ress += (-num[i]); //将所有的负数取反
}
for(int i=fuNum;i<num.length;i++){
ress += num[i];
}
return ress;
}
}