(贪心) LeetCode 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 次,但是可以选择同一元素多次操作。

(1)聪明的小伙伴已经猜到了,只要我每次将最小的一个数选择置反,然后操作k 次,就可以得到最大的和了。没错,这种做法完全正确,我们直接使用优先队列小根堆来存储数组元素,然后每次取出堆头元素置反再添加回去,这样k 次即可。

(2)我们再来看看再原数组中如何操作,细心的同学可以发现,我们只需要在有限的k 次操作中将绝对值大的负数置反,这样就能使得最终结果尽可能的大一些,如果在将所有负数置反之后发现k 任有剩余,那么我们只需要选择一个最小的元素操作k 次就可以了,这样就能使得答案最大,那么我们怎么操作数组呢,难不成每操作一个元素就排序一次吗?这样的做法还不如使用优先队列呢,其实不然,我们只需要在排序的时候做一些操作即可,按照数组中数的绝对值从大到小排序即可 ,例如(1,5, 2, -8,-4, k = 5),这样排序之后就可以得到(-8,5,-4,2,1,k = 5);然后我们只需要从头遍历数组,优先处理负数,在k 次范围内将绝对值最大的负数置反即可,遍历结束之后,如果k 任有剩余,只需要将数组末尾的元素(排序之后末尾的就是最小的)执行k 次操作即可,有些同学想要使用while(k--)来操作,然后反复将最后一位元素进行置反,完全没必要,如果剩余的k 是偶数的话,就不用管了,因为在k 次操作之后值是不会变的,如果是奇数,只需要将值 *-1 即可。

最后遍历优先队列或者数组将总和求出返回即可。

话不多说!!!上代码!!

三. 代码

  1. 使用优先队列:
cpp 复制代码
class Solution {
public:
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        priority_queue<int, vector<int>, greater<>> que;
        for(int i = 0; i < nums.size(); i++){
            que.push(nums[i]);
        }
        while(k--){
            int node = que.top(); que.pop();
            que.push(-node);
        }
        int res = 0;
        while(!que.empty()){
            int node = que.top(); que.pop();
            res += node;
        }
        return res;
    }
};
  1. 在原数组中操作:
cpp 复制代码
class Solution {
public:
    static bool com(int a, int b){
        return abs(a) > abs(b);
    }
    int largestSumAfterKNegations(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end(), com);
        for(int i = 0 ; i< nums.size(); i++){
            if(nums[i] < 0 && k > 0){
                nums[i] *= -1;
                k--;
            }
        }
        if(k % 2 == 1) nums[nums.size() - 1] *= -1; 
        int res = 0;
        for(int i = 0; i < nums.size(); i++){
            res += nums[i];
        }
        return res;
    }
};

四. 总结

本题两种做法都能通过,但是我们更将以使用后面的做法,因为在时间以及空间复杂度上都简洁了很多,贪心本就是需要自己锻炼的一种思想,如果使用库函数的话,就没有很好的贪心思想体现了。

(1). 优先队列时间复杂度:O(nlogn) ;空间复杂度:O(n)。

(2). 原数组操作时间复杂度:O(nlogn); 空间复杂度:O(1)。

喜欢的话给个关注吧!!

相关推荐
爱coding的橙子2 小时前
每日算法刷题 Day3 5.11:leetcode数组2道题,用时1h(有点慢)
算法·leetcode
虾球xz4 小时前
游戏引擎学习第268天:合并调试链表与分组
c++·学习·链表·游戏引擎
fpcc4 小时前
跟我学c++高级篇——模板元编程之十三处理逻辑
c++
格林威5 小时前
Baumer工业相机堡盟工业相机的工业视觉中为什么偏爱“黑白相机”
开发语言·c++·人工智能·数码相机·计算机视觉
Dream it possible!6 小时前
LeetCode 热题 100_只出现一次的数字(96_136_简单_C++)(哈希表;哈希集合;排序+遍历;位运算)
c++·leetcode·位运算·哈希表·哈希集合
?abc!7 小时前
缓存(5):常见 缓存数据淘汰算法/缓存清空策略
java·算法·缓存
BioRunYiXue7 小时前
一文了解氨基酸的分类、代谢和应用
人工智能·深度学习·算法·机器学习·分类·数据挖掘·代谢组学
Dddle17 小时前
C++:this指针
java·c语言·开发语言·c++
不見星空8 小时前
2025年第十六届蓝桥杯软件赛省赛C/C++大学A组个人解题
c语言·c++·蓝桥杯
jiunian_cn8 小时前
【c++】异常详解
java·开发语言·数据结构·c++·算法·visual studio