力扣第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++ 代码一

cpp 复制代码
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++ 代码二

cpp 复制代码
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;
    }
}

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

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

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

相关推荐
UestcXiye1 小时前
《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列
c++·计算机网络·ip·tcp
好奇龙猫2 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
霁月风2 小时前
设计模式——适配器模式
c++·适配器模式
sp_fyf_20242 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
ChoSeitaku3 小时前
链表交集相关算法题|AB链表公共元素生成链表C|AB链表交集存放于A|连续子序列|相交链表求交点位置(C)
数据结构·考研·链表
偷心编程3 小时前
双向链表专题
数据结构
香菜大丸3 小时前
链表的归并排序
数据结构·算法·链表
jrrz08283 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
oliveira-time3 小时前
golang学习2
算法
咖啡里的茶i3 小时前
Vehicle友元Date多态Sedan和Truck
c++