力扣第1005题 K 次取反后最大化的数组和 c++ 贪心 双思维

题目

1005. 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 <= 104
  • -100 <= nums[i] <= 100
  • 1 <= k <= 104

思路和解题方法

  •         首先,我们需要对数组进行排序。由于是要使数组中的数尽可能地都为正数,因此我们应该把绝对值小的负数变为正数。
  •         这样一来,负数的数量就会减少,而整数和零的数量就会增加,这有利于最终结果更接近最优解。
  •         排序后,我们可以从小到大遍历数组,每当遇到一个负数,就将其取反,同时减少可取反的次数 k。
  •         这里有个问题,如果我们仅仅只考虑绝对值最小的那个负数,需要取反多少次呢?显然,如果可取反的次数 k 为奇数,那么最终结果就是把绝对值最小的那个负数取反,而如果可取反的次数 k 为偶数,则不需要取反它。
  •         另一方面,如果可取反的次数 k 为偶数,那么显然数组中所有的数都会保持不变。最后,我们只需简单地处理一下数组的和即可。

复杂度

        时间复杂度:

                O(n * logn)

        时间复杂度:排序的时间复杂度为 O(nlogn),for 循环的时间复杂度为 O(n),因此总的时间复杂度为 O(nlogn + nlogn + n) = O(nlogn)。

        空间复杂度

                O(1)

        空间复杂度:除了输入的数组外,算法只涉及到常量级别的额外空间。因此空间复杂度为 O(1)。

c++ 代码一

class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end()); // 对数组进行排序,使得负数排在前面
        int min1 = 1000; // 初始化绝对值最小的元素为一个较大的数
        int min2 = 0; // 记录绝对值最小的元素的索引

        for (int i=0; i<nums.size(); i++) {
            if(abs(nums[i]) <= min1) {  // 如果当前元素的绝对值小于等于min1
                min1 = abs(nums[i]); // 更新min1为当前元素的绝对值
                min2 = i; // 记录绝对值最小的元素的索引
            }

            if(nums[i] < 0 && k > 0) { // 如果当前元素是负数且还有剩余的翻转次数
                nums[i] *= -1; // 将当前元素取反
                k--; // 翻转次数k减一
            }
        }

        if(k%2 == 1) // 如果剩余的翻转次数是奇数
            nums[min2] *= -1; // 将绝对值最小的元素取反

        int ans = 0;
        for(int n : nums)
            ans += n; // 计算数组中所有元素的和

        return ans; // 返回最终的数组和作为结果
    }
};

思路和解题方法二

  • 对数组进行排序
  • 排序函数中采用自定义比较器的方式,把按照绝对值从大到小进行排序。这样排序后,数组中绝对值最大的元素会排在数组的最末尾,而绝对值最小的元素则会排在数组的最前面。

  • 取反负数
  • 遍历数组,如果当前的元素是负数,那么就把它取反(变为正数),同时将剩余可取反次数减一。注意我们要在剩余可取反次数大于 0 且当前元素是负数的情况下才能取反。

  • 处理无法取反的情况
  • 如果我们完成了步骤 2 后,还有剩余可取反的次数,但已经不存在可以被取反的元素了,那么我们需要对数组进行调整,使得我们所取反的元素的绝对值最小。具体地说,我们需要在数组的最末尾找到一个元素,并将它取反。因为这个元素绝对值最大,所以取反后对原来的和的影响最小。由于我们对数组进行了排序,因此直接访问最末尾的元素即可。

  • 计算数组的和
  • 遍历整个数组,计算所有元素之和即可。最终的和就是我们的答案。

复杂度

        时间复杂度:

                O(n * logn)

        时间复杂度:排序的时间复杂度为 O(nlogn),for 循环的时间复杂度为 O(n),因此总的时间复杂度为 O(nlogn + nlogn + n) = O(nlogn)。

        空间复杂度

                O(1)

        空间复杂度:除了输入的数组外,算法只涉及到常量级别的额外空间。因此空间复杂度为 O(1)。

c++ 代码二

class Solution {
    // 定义排序比较器,按照绝对值从大到小排序
    static bool cmp(int a, int b) {
        return abs(a) > abs(b);
    }
public:
    int largestSumAfterKNegations(vector<int>& A, int K) {
        sort(A.begin(), A.end(), cmp);       // 第一步:对数组进行排序
        
        for (int i = 0; i < A.size(); i++) { // 第二步:取反负数
            if (A[i] < 0 && K > 0) {
                A[i] *= -1;
                K--;
            }
        }
        
        if (K % 2 == 1) A[A.size() - 1] *= -1; // 第三步:处理无法取反的情况
        
        int result = 0;
        for (int a : A) result += a;        // 第四步:计算数组和
        
        return result;
    }
}

觉得有用的话可以点点赞,支持一下。

如果愿意的话关注一下。会对你有更多的帮助。

每天都会不定时更新哦  >人<  。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值