https://leetcode-cn.com/problems/maximize-sum-of-array-after-k-negations/
给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)
以这种方式修改数组后,返回数组可能的最大和。
示例 1:
输入:A = [4,2,3], K = 1
输出:5
解释:选择索引 (1,) ,然后 A 变为 [4,-2,3]。
示例 2:
输入:A = [3,-1,0,2], K = 3
输出:6
解释:选择索引 (1, 2, 2) ,然后 A 变为 [3,1,0,2]。
示例 3:
输入:A = [2,-3,-1,5,-4], K = 2
输出:13
解释:选择索引 (1, 4) ,然后 A 变为 [2,3,-1,5,4]。
提示:
1 <= A.length <= 10000
1 <= K <= 10000
-100 <= A[i] <= 100
小菜鸡的尝试:
通过样例的设计,研究出可分为以下几种情况:
1.操作次数大于负数个数
1)操作次数 - 负数个数 是偶数 全部的负数都可以转正
2)操作次数 - 负数个数 是奇数 绝对值最小的正数/负数 需要变成负数
2.操作次数小于负数个数(绝对值小的负数不能转正)
主要通过时间换空间的思想,使用优先队列将非正数和正数分别使用两个优先队列储存从小到大,从大到小的排序存储。
1 class Solution { 2 public: 3 int largestSumAfterKNegations(vector<int>& A, int K) { 4 priority_queue<int> a; // 记录负数 5 priority_queue<int, vector<int>, greater<int> > b; // 记录负数 6 priority_queue<int, vector<int>, greater<int> > c; // 记录正数 7 int result = 0; 8 for (int i = 0; i < A.size(); i ++) { 9 if (A[i] <= 0) { 10 b.push(A[i]); 11 a.push(A[i]); 12 } else { 13 c.push(A[i]); 14 } 15 } 16 cout << "K=" << K << endl; 17 cout << "非正元素的个数" << b.size() << endl; 18 if (K <= b.size()) { // 不一定所有负数都能转正 19 while (K) { // 加入转正的负数 20 result = result - b.top(); 21 b.pop(); 22 K--; 23 } 24 while (!b.empty()){ // 加入不能转正的负数 25 result = result + b.top(); 26 b.pop(); 27 } 28 while (!c.empty()) { //加入正数 29 result = result + c.top(); 30 c.pop(); 31 } 32 return result; 33 } 34 if (K > b.size()) { // 可能有正数要变负或者最小负数不能转正 35 if ((K - b.size()) % 2 == 0) { // 所有负数可变正数 36 cout << "1" << endl; 37 while (!b.empty()){ // 加入转正的负数 38 result = result - b.top(); 39 b.pop(); 40 } 41 while (!c.empty()) { //加入正数 42 result = result + c.top(); 43 c.pop(); 44 } 45 return result; 46 } 47 if ((K - b.size()) % 2 != 0) { // 可能有正数要变负或者最小负数不能转正 48 if (b.empty() || (-1 * a.top()) > c.top()) { // 最小正数要变负 49 result = result - c.top(); // 加入最小正数转负后的值 50 c.pop(); 51 while (!b.empty()){ // 加入转正的负数 52 result = result - b.top(); 53 b.pop(); 54 } 55 while (!c.empty()) { //加入正数 56 result = result + c.top(); 57 c.pop(); 58 } 59 return result; 60 } 61 if ((-1 * a.top()) <= c.top()) { // 最大负数不能转正 62 cout << "最大负数不能转正" << endl; 63 result = result + a.top(); // 加入最大负数 64 a.pop(); 65 while (!a.empty()){ // 加入转正的负数 66 result = result - a.top(); 67 a.pop(); 68 } 69 while (!c.empty()) { //加入正数 70 result = result + c.top(); 71 c.pop(); 72 } 73 return result; 74 } 75 76 } 77 } 78 return result; 79 } 80 };
小菜鸡做了一点点优化,在第一遍遍历压入优先队列的时候就计算所有数据的绝对值之和,而在分情况的时候在进行修正,可以略微减少时间消耗
1 class Solution { 2 public: 3 int largestSumAfterKNegations(vector<int>& A, int K) { 4 priority_queue<int> a; // 记录负数 5 priority_queue<int, vector<int>, greater<int> > b; // 记录负数 6 priority_queue<int, vector<int>, greater<int> > c; // 记录正数 7 priority_queue<int> d; // 记录正数 8 int result = 0; 9 for (int i = 0; i < A.size(); i ++) { 10 if (A[i] <= 0) { 11 b.push(A[i]); 12 a.push(A[i]); 13 result = result - A[i]; 14 } else { 15 c.push(A[i]); 16 d.push(A[i]); 17 result = result + A[i]; 18 } 19 } 20 cout << "K=" << K << endl; 21 cout << "非正元素的个数" << b.size() << endl; 22 if (K <= b.size()) { // 不一定所有负数都能转正 23 while (b.size() - K) { // 加入不能转正的负数 24 result = result + 2 * a.top(); 25 a.pop(); 26 K ++; 27 } 28 return result; 29 } 30 if (K > b.size()) { // 可能有正数要变负或者最小负数不能转正 31 if ((K - b.size()) % 2 == 0) { // 所有负数可变正数 32 return result; 33 } 34 if ((K - b.size()) % 2 != 0) { // 可能有正数要变负或者最小负数不能转正 35 if (b.empty() || (-1 * a.top()) > c.top()) { // 最小正数要变负 36 result = result - 2 * c.top(); // 加入最小正数转负后的值 37 return result; 38 } 39 if ((-1 * a.top()) <= c.top()) { // 最大负数不能转正 40 result = result + 2 * a.top(); // 加入最大负数 41 return result; 42 } 43 } 44 } 45 return result; 46 } 47 };
膜拜大佬的代码:
总体思路是一样的,他用sort方法直接对原数据进行改动,减去优先队列的空间开销。
1 int largestSumAfterKNegations(vector<int>& A, int K) { 2 sort(A.begin(),A.end()); // 用sort函数来替换优先队列的空间开销 3 if (A[0] >= 0) { // 最小正数 4 if (K % 2 != 0) A[0] = -A[0]; 5 return accumulate(A.begin(),A.end(),0); 6 } 7 for (int i = 0; i < K; i ++) { 8 // 非正数优先转正,越小越优先 9 if (A[i] < 0) { // 负数逐个转正 10 A[i] = -A[i]; 11 } else if (A[i] == 0) { 12 break; 13 // 正数的操作 14 } else { 15 if ((K-i) % 2 == 0) break; // 可以通过两次重复操作来消除 16 if (A[i] < A[i-1]) { 17 A[i] = -A[i]; 18 } else { 19 A[i-1] = -A[i-1]; 20 } 21 break; 22 } 23 } 24 return accumulate(A.begin(),A.end(),0); 25 }
还看见了一个相同思路但是表述更加简洁的一个操作
1 int largestSumAfterKNegations(vector<int> &A, int K) { 2 sort(A.begin(), A.end()); // 排序 3 int ans = 0; 4 for (int i = 0; i < A.size(); i++) { 5 if (A[i] < 0 && K > 0) { // 先尽可能将所有负数转正 6 A[i] = -A[i]; 7 K--; 8 } 9 } 10 sort(A.begin(), A.end()); // 转正后再排一次序 11 while (K > 0) { // 对转正后的最小的数进行操作 12 A[0] = -A[0]; 13 K--; 14 } 15 for (int i = 0; i < A.size(); i++) { // 做一次加法 16 ans += A[i]; 17 } 18 return ans; 19 }