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

    1. class Solution {
    2. public:
    3. int largestSumAfterKNegations(vector<int>& nums, int k) {
    4. sort(nums.begin(), nums.end()); // 对数组进行排序,使得负数排在前面
    5. int min1 = 1000; // 初始化绝对值最小的元素为一个较大的数
    6. int min2 = 0; // 记录绝对值最小的元素的索引
    7. for (int i=0; isize(); i++) {
    8. if(abs(nums[i]) <= min1) { // 如果当前元素的绝对值小于等于min1
    9. min1 = abs(nums[i]); // 更新min1为当前元素的绝对值
    10. min2 = i; // 记录绝对值最小的元素的索引
    11. }
    12. if(nums[i] < 0 && k > 0) { // 如果当前元素是负数且还有剩余的翻转次数
    13. nums[i] *= -1; // 将当前元素取反
    14. k--; // 翻转次数k减一
    15. }
    16. }
    17. if(k%2 == 1) // 如果剩余的翻转次数是奇数
    18. nums[min2] *= -1; // 将绝对值最小的元素取反
    19. int ans = 0;
    20. for(int n : nums)
    21. ans += n; // 计算数组中所有元素的和
    22. return ans; // 返回最终的数组和作为结果
    23. }
    24. };

    思路和解题方法二

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

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

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

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

    复杂度

            时间复杂度:

                    O(n * logn)

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

            空间复杂度

                    O(1)

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

    c++ 代码二

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

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

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

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

  • 相关阅读:
    Java学习笔记(二):封装、继承、多态
    请求合并与拆分在并发场景中应用
    基于casbin的RBAC权限实践
    [和ChatGPT学编程]Python Requests 简介
    谷歌浏览器误代码STATUS_INVALID_IMAGE_HASH如何一行代码解决
    PDE优化|逆问题中偏微分方程约束优化的惩罚方法(Matlab代码实现)
    云原生原则及关键技术
    C++ Tutorials: C++ Language: Standard library: Input/output with files
    【D1 Dock Pro开发板】LED灯闪烁
    基于JAVA基于MVC框架的在线书店设计计算机毕业设计源码+系统+mysql数据库+lw文档+部署
  • 原文地址:https://blog.csdn.net/jgk666666/article/details/134000085