1005. Maximum array sum after K negations
LeetCode: 1005. Maximum array sum after K negations
simple single \color{#00AF9B} {simple} simple
Give you an integer array nums and an integer k. modify the array as follows:
- Select a subscript i and replace num [i] with - num [i].
Repeat this process exactly k times. You can select the same subscript i multiple times.
When the array is modified in this way, the maximum possible sum of the array is returned.
Example 1:
Input: nums = [4,2,3], k = 1 Output: 5 Explanation: select subscript 1, nums Become [4,-2,3] .
Example 2:
Input: nums = [3,-1,0,2], k = 3 Output: 6 Explanation: select subscript (1, 2, 2) ,nums Become [3,1,0,2] .
Example 3:
Input: nums = [2,-3,-1,5,-4], k = 2 Output: 13 Explanation: select subscript (1, 4) ,nums Become [2,3,-1,5,4] .
Tips:
- 1 <= nums.length <= 104
- -100 <= nums[i] <= 100
- 1 <= k <= 104
Method 1: greed + sorting
For all stored negative numbers in the array, you can negate at most k times, and each time you negate, you should try to select smaller negative numbers (greedy). In this way, the remaining large negative numbers have less negative impact on the maximum sum; The negative number with small value changes into a positive number with large value, which has the greatest positive impact on the maximum sum.
-
If k operations are filled, regardless of whether there are negative numbers in the array, it is already the possible maximum sum. Because the remaining negative numbers have the greatest and least negative impact.
-
If all negative numbers are reversed, that is, at this time, the array is positive, but there are still remaining times to do. Let's assume that done negation has been completed, then the remaining times are k-done. At this time, there are two cases:
- If k-done is even. Since the same number can be negated many times, we can attach all the even numbers to any same number abstractly. Since the inverse of an even number of times is still itself, such an array is still the original array. Because the array is positive at this time, it is the determined maximum sum.
- If k-done is odd. Similarly, the even number is appended to the same number. In this way, there is only one negation left. Of course, we hope to negate the smallest positive number, so as to minimize the negative impact on the maximum sum.
In order to better locate the elements, we can sort first, and sort according to the absolute value of each element from small to large.
#include <vector> #include <algorithm> #include <numeric> using namespace std; class Solution { public: int largestSumAfterKNegations(vector<int> &nums, int k) { const int n = nums.size(); sort(nums.begin(), nums.end(), [](int &left, int &right) { return abs(left) < abs(right); }); int done = 0; for (int i = n - 1; i >= 0; i--) { if (nums[i] < 0 && done < k) { nums[i] = -nums[i]; done++; if (done == k) break; } } if ((k - done) % 2 != 0) { for (int i = 0; i < n; i++) { if (nums[i] >= 0) { nums[i] = -nums[i]; break; } } } return accumulate(nums.begin(), nums.end(), 0); } };
Complexity analysis
-
Time complexity: O ( n log n ) O(n \log n) O(nlogn). std::sort is quick sort, and the time complexity is O ( n log n ) O(n \log n) O(nlogn); Each element in the array can be traversed up to 2 times. Therefore, the time complexity is O ( n log n ) + O ( 2 n ) = O ( n log n ) O(n \log n)+O(2n)=O(n \log n) O(nlogn)+O(2n)=O(nlogn)
-
Space complexity: O ( log n ) O(\log n) O(logn). Mainly for the recursive consumption of quick sorting.
Reference results
Accepted 80/80 cases passed (4 ms) Your runtime beats 89.82 % of cpp submissions Your memory usage beats 55.63 % of cpp submissions (8.8 MB)
Method 2: greed + hash table
The range of elements in the array given in the title is: - 100 < = num [i] < = 100. It can be found that there are only 201 numbers in total, and the order of magnitude is small. Therefore, if we count each element, the length of the hash table is only 201.
We assume that there is a number axis with a range of [- 100, 100], and the elements on the number axis represent the count of the number. If we count each number, for each negative number, the operation of negating it becomes: the negative number count minus 1, and the corresponding integer count plus 1. Finally, when summing, we add each number whose count is not 0 by multiplying its subscript and count.
Since the number axis itself is arranged from small to large, our counting implies the law from small to large, because the count value is directly related to its subscript time. Therefore, we can directly locate the smallest negative number and the smallest positive number, which saves the sorting time.
array
We can define an array with a length of 201, and the initial value of each item is 0. Since the beginning of the array subscript is 0 and the value in this question reaches - 100, a certain conversion is required. Set Changshu pivot=100 as the counting position of element 0, so [- 100, - 1] corresponds to [0,99] in the hash table, and [1,100] corresponds to [101,200] in the hash table.
#include <vector> using namespace std; class Solution { public: int largestSumAfterKNegations(vector<int> &nums, int k) { const int n = nums.size(); int *cnts = new int[201]{}; const int pivot = 100; for (const int num : nums) { cnts[num + pivot]++; } for (int i = -100; i < 0; i++) { if (cnts[i + pivot] != 0) { int ops = min(k, cnts[i + pivot]); cnts[i + pivot] -= ops; cnts[-i + pivot] += ops; k -= ops; if (k == 0) break; } } if (k > 0 && k % 2 == 1 && cnts[pivot] == 0) { for (int i = 1; i <= 100; i++) { if (cnts[i + pivot] != 0) { cnts[i + pivot]--; cnts[-i + pivot]++; break; } } } int ans = 0; for (int i = -100; i <= 100; i++) { if (cnts[i + pivot] != 0) { ans += i * cnts[i + pivot]; } } return ans; } };
std::unordered_map
We can also use STD:: unordered in STL_ Map, which eliminates the need to relocate with pivot.
#include <vector> #include <unordered_map> using namespace std; class Solution { public: int largestSumAfterKNegations(vector<int> &nums, int k) { const int n = nums.size(); unordered_map<int, int> cnts; for (const int num : nums) { cnts[num]++; } for (int i = -100; i < 0; i++) { if (cnts[i] != 0) { int ops = min(k, cnts[i]); cnts[i] -= ops; cnts[-i] += ops; k -= ops; if (k == 0) break; } } if (k > 0 && k % 2 == 1 && cnts[0] == 0) { for (int i = 1; i <= 100; i++) { if (cnts[i] != 0) { cnts[i]--; cnts[-i]++; break; } } } int ans = 0; for (int i = -100; i <= 100; i++) { if (cnts[i] != 0) { ans += i * cnts[i]; } } return ans; } };
Complexity analysis
-
Time complexity: O ( n + C ) O(n+C) O(n+C). Where n is the length of the array and C is the range of elements in num. in this problem, C is 201.
-
Space complexity: O ( C ) O(C) O(C). Mainly the overhead of hash table.
Reference results
Accepted 80/80 cases passed (8 ms) Your runtime beats 44.56 % of cpp submissions Your memory usage beats 5.17 % of cpp submissions (9.9 MB)