2098. Subsequence of Size K With the Largest Even Sum π
Problem Description
You need to find the largest possible even sum from a subsequence of an integer array nums
that has exactly k
elements.
Given:
- An integer array
nums
- An integer
k
Task:
- Select exactly
k
elements fromnums
(maintaining their relative order as a subsequence) - Calculate the sum of these
k
elements - Find the maximum sum that is even
- Return this maximum even sum, or
-1
if no even sum is possible
A subsequence means you can pick elements from the original array by skipping some elements, but you cannot change the order of the elements you pick. For example, from [1,2,3,4]
, you can pick [1,3,4]
as a subsequence, but not [3,1,4]
.
The solution approach sorts the array first and greedily selects the largest k
numbers. If their sum is odd, it tries to make it even by:
- Replacing the smallest even number in the selected
k
with the largest odd number from the remaining elements - Replacing the smallest odd number in the selected
k
with the largest even number from the remaining elements
The algorithm chooses whichever replacement gives the larger even sum, or returns -1
if no even sum is achievable.
Intuition
Since we're looking for a subsequence (not a subarray), the order of elements doesn't matter for calculating the sum. This gives us the freedom to sort the array, which immediately suggests a greedy approach.
To maximize the sum, we naturally want to pick the k
largest numbers. After sorting, these would be the last k
elements. If their sum is even, we're done - this is the maximum possible even sum.
But what if the sum of the k
largest numbers is odd? We need to make it even somehow.
The key insight is that to change a sum from odd to even (or vice versa), we need to either:
- Add/subtract an odd number
- Add/subtract an even number that changes the parity
Since we must maintain exactly k
elements, we can't just add or remove - we need to replace. This means swapping one element from our selected k
with one from the remaining n-k
elements.
To keep the sum as large as possible while making it even, we want to:
- Remove the smallest element from our selection
- Add the largest element from the remaining pool
- Ensure this swap changes the parity from odd to even
This leads to two scenarios:
- If we remove an even number (the smallest even in our
k
), we must add an odd number (the largest odd from remaining) to change parity - If we remove an odd number (the smallest odd in our
k
), we must add an even number (the largest even from remaining) to change parity
We try both swaps and pick whichever gives us the larger even sum. If neither swap is possible (e.g., all k
selected numbers are odd and all remaining are odd too), then no even sum exists and we return -1
.
Solution Approach
The implementation follows the greedy strategy with these steps:
Step 1: Sort the array
nums.sort()
This allows us to easily identify the largest k
numbers and work with them.
Step 2: Calculate initial sum of largest k numbers
ans = sum(nums[-k:])
We take the last k
elements after sorting, which are the largest ones.
Step 3: Check if sum is already even
if ans % 2 == 0: return ans
If the sum is even, we've found our answer immediately.
Step 4: Find replacement candidates from remaining elements
mx1 = mx2 = -inf for x in nums[: n - k]: if x & 1: # if x is odd mx1 = x else: # if x is even mx2 = x
We iterate through the first n-k
elements (those not selected) to find:
mx1
: the largest odd numbermx2
: the largest even number
Since the array is sorted, the last occurrence of each type will be the maximum.
Step 5: Find elements to replace from selected k numbers
mi1 = mi2 = inf for x in nums[-k:][::-1]: if x & 1: # if x is odd mi2 = x else: # if x is even mi1 = x
We iterate through the selected k
numbers in reverse order to find:
mi1
: the smallest even numbermi2
: the smallest odd number
The reverse iteration [::-1]
ensures we find the smallest values first (since the array is sorted).
Step 6: Calculate possible even sums
ans = max(ans - mi1 + mx1, ans - mi2 + mx2, -1)
We consider two possible swaps:
- Replace smallest even (
mi1
) with largest odd (mx1
):ans - mi1 + mx1
- Replace smallest odd (
mi2
) with largest even (mx2
):ans - mi2 + mx2
Both swaps change the parity from odd to even. We take the maximum of these two options and -1
.
Step 7: Return result
return -1 if ans < 0 else ans
If ans
is negative (which happens when no valid swap exists because mx1
or mx2
remained at -inf
, or mi1
or mi2
remained at inf
), we return -1
. Otherwise, we return the maximum even sum found.
The time complexity is O(n log n)
due to sorting, and the space complexity is O(1)
as we only use a few variables.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through the algorithm with nums = [4, 1, 5, 3, 1]
and k = 3
.
Step 1: Sort the array
- After sorting:
nums = [1, 1, 3, 4, 5]
Step 2: Select the k largest numbers
- The 3 largest numbers are the last 3 elements:
[3, 4, 5]
- Initial sum:
3 + 4 + 5 = 12
Step 3: Check if sum is even
12 % 2 = 0
, so the sum is already even- Return
12
immediately
Let's try another example where we need to make adjustments: nums = [2, 3, 8, 7, 1]
and k = 3
.
Step 1: Sort the array
- After sorting:
nums = [1, 2, 3, 7, 8]
Step 2: Select the k largest numbers
- The 3 largest numbers:
[3, 7, 8]
- Initial sum:
3 + 7 + 8 = 18
(even, so return 18)
Let's try one more where the initial sum is odd: nums = [1, 2, 3, 4]
and k = 2
.
Step 1: Sort the array
- Already sorted:
nums = [1, 2, 3, 4]
Step 2: Select the k largest numbers
- The 2 largest numbers:
[3, 4]
- Initial sum:
3 + 4 = 7
(odd)
Step 3: Find replacement candidates from remaining elements
- Remaining elements (not in our k selection):
[1, 2]
- Largest odd from remaining:
mx1 = 1
- Largest even from remaining:
mx2 = 2
Step 4: Find elements to replace from selected k
- Selected elements:
[3, 4]
- Smallest even from selected:
mi1 = 4
- Smallest odd from selected:
mi2 = 3
Step 5: Calculate possible swaps
- Option 1: Replace even 4 with odd 1:
7 - 4 + 1 = 4
(even) - Option 2: Replace odd 3 with even 2:
7 - 3 + 2 = 6
(even) - Maximum even sum:
max(4, 6) = 6
Step 6: Return result
- Return
6
The algorithm correctly identifies that swapping 3 with 2 gives us the maximum even sum of 6 from selecting exactly 2 elements.
Solution Implementation
1class Solution:
2 def largestEvenSum(self, nums: List[int], k: int) -> int:
3 # Sort numbers in ascending order
4 nums.sort()
5
6 # Calculate sum of k largest numbers
7 initial_sum = sum(nums[-k:])
8
9 # If sum is already even, return it
10 if initial_sum % 2 == 0:
11 return initial_sum
12
13 n = len(nums)
14
15 # Find the largest odd and even numbers from the unselected portion (first n-k elements)
16 max_odd_unselected = max_even_unselected = float('-inf')
17 for num in nums[:n - k]:
18 if num & 1: # Check if odd using bitwise AND
19 max_odd_unselected = num
20 else:
21 max_even_unselected = num
22
23 # Find the smallest odd and even numbers from the selected portion (last k elements)
24 min_even_selected = min_odd_selected = float('inf')
25 for num in nums[-k:][::-1]: # Iterate in reverse to find smallest first
26 if num & 1: # Check if odd
27 min_odd_selected = num
28 else:
29 min_even_selected = num
30
31 # Try two possible swaps to make sum even:
32 # 1. Replace smallest even in selection with largest odd from unselected
33 # 2. Replace smallest odd in selection with largest even from unselected
34 best_sum = max(
35 initial_sum - min_even_selected + max_odd_unselected,
36 initial_sum - min_odd_selected + max_even_unselected,
37 -1
38 )
39
40 # Return -1 if no valid even sum exists, otherwise return the best sum
41 return -1 if best_sum < 0 else best_sum
42
1class Solution {
2 public long largestEvenSum(int[] nums, int k) {
3 // Sort array in ascending order
4 Arrays.sort(nums);
5
6 // Calculate sum of k largest elements
7 long sum = 0;
8 int n = nums.length;
9 for (int i = 0; i < k; ++i) {
10 sum += nums[n - i - 1];
11 }
12
13 // If sum is already even, return it
14 if (sum % 2 == 0) {
15 return sum;
16 }
17
18 // Sum is odd, need to swap one element to make it even
19 // To make odd sum even: replace odd with even or even with odd
20
21 final int INF = 1 << 29;
22
23 // Find largest odd and even numbers from unused elements (first n-k elements)
24 int largestUnusedOdd = -INF;
25 int largestUnusedEven = -INF;
26 for (int i = 0; i < n - k; ++i) {
27 if (nums[i] % 2 == 1) {
28 largestUnusedOdd = nums[i];
29 } else {
30 largestUnusedEven = nums[i];
31 }
32 }
33
34 // Find smallest odd and even numbers from selected elements (last k elements)
35 int smallestSelectedOdd = INF;
36 int smallestSelectedEven = INF;
37 for (int i = n - 1; i >= n - k; --i) {
38 if (nums[i] % 2 == 1) {
39 smallestSelectedOdd = nums[i];
40 } else {
41 smallestSelectedEven = nums[i];
42 }
43 }
44
45 // Try two swapping strategies:
46 // 1. Replace smallest selected even with largest unused odd
47 // 2. Replace smallest selected odd with largest unused even
48 long maxEvenSum = Math.max(
49 sum - smallestSelectedEven + largestUnusedOdd,
50 sum - smallestSelectedOdd + largestUnusedEven
51 );
52
53 // Return -1 if no valid even sum exists
54 return maxEvenSum < 0 ? -1 : maxEvenSum;
55 }
56}
57
1class Solution {
2public:
3 long long largestEvenSum(vector<int>& nums, int k) {
4 // Sort the array in ascending order
5 sort(nums.begin(), nums.end());
6
7 // Calculate the sum of k largest elements
8 long long sum = 0;
9 int n = nums.size();
10 for (int i = 0; i < k; ++i) {
11 sum += nums[n - i - 1];
12 }
13
14 // If the sum is already even, return it
15 if (sum % 2 == 0) {
16 return sum;
17 }
18
19 // Sum is odd, need to make one swap to make it even
20 // We need to either:
21 // 1. Replace an odd number from selected with an even number from unselected
22 // 2. Replace an even number from selected with an odd number from unselected
23
24 const int INF = 1 << 29;
25
26 // Find the largest odd and even numbers from unselected elements (first n-k elements)
27 int largestUnselectedOdd = -INF;
28 int largestUnselectedEven = -INF;
29 for (int i = 0; i < n - k; ++i) {
30 if (nums[i] % 2 == 1) {
31 largestUnselectedOdd = nums[i];
32 } else {
33 largestUnselectedEven = nums[i];
34 }
35 }
36
37 // Find the smallest odd and even numbers from selected elements (last k elements)
38 int smallestSelectedOdd = INF;
39 int smallestSelectedEven = INF;
40 for (int i = n - 1; i >= n - k; --i) {
41 if (nums[i] % 2 == 1) {
42 smallestSelectedOdd = nums[i];
43 } else {
44 smallestSelectedEven = nums[i];
45 }
46 }
47
48 // Try both possible swaps and take the maximum
49 // Swap 1: Replace smallest selected even with largest unselected odd
50 // Swap 2: Replace smallest selected odd with largest unselected even
51 long long maxEvenSum = max(sum - smallestSelectedEven + largestUnselectedOdd,
52 sum - smallestSelectedOdd + largestUnselectedEven);
53
54 // If no valid swap exists (result is negative), return -1
55 return maxEvenSum < 0 ? -1 : maxEvenSum;
56 }
57};
58
1/**
2 * Finds the largest even sum by selecting exactly k numbers from the array
3 * @param nums - Array of integers to select from
4 * @param k - Number of elements to select
5 * @returns The largest even sum possible, or -1 if no even sum exists
6 */
7function largestEvenSum(nums: number[], k: number): number {
8 // Sort the array in ascending order
9 nums.sort((a: number, b: number) => a - b);
10
11 // Initialize the sum with the k largest numbers
12 let currentSum: number = 0;
13 const arrayLength: number = nums.length;
14
15 for (let i = 0; i < k; i++) {
16 currentSum += nums[arrayLength - i - 1];
17 }
18
19 // If the sum is already even, return it as the maximum
20 if (currentSum % 2 === 0) {
21 return currentSum;
22 }
23
24 // The sum is odd, need to make one swap to make it even
25 // We need to either:
26 // 1. Replace an odd number from selected with an even number from unselected
27 // 2. Replace an even number from selected with an odd number from unselected
28
29 const INFINITY: number = 1 << 29;
30
31 // Find the largest odd and even numbers from the unselected portion (first n-k elements)
32 let largestUnselectedOdd: number = -INFINITY;
33 let largestUnselectedEven: number = -INFINITY;
34
35 for (let i = 0; i < arrayLength - k; i++) {
36 if (nums[i] % 2 === 1) {
37 largestUnselectedOdd = nums[i];
38 } else {
39 largestUnselectedEven = nums[i];
40 }
41 }
42
43 // Find the smallest even and odd numbers from the selected portion (last k elements)
44 let smallestSelectedEven: number = INFINITY;
45 let smallestSelectedOdd: number = INFINITY;
46
47 for (let i = arrayLength - 1; i >= arrayLength - k; i--) {
48 if (nums[i] % 2 === 1) {
49 smallestSelectedOdd = nums[i];
50 } else {
51 smallestSelectedEven = nums[i];
52 }
53 }
54
55 // Calculate the maximum even sum by considering both swap options
56 // Option 1: Replace smallest selected even with largest unselected odd
57 // Option 2: Replace smallest selected odd with largest unselected even
58 const maxEvenSum: number = Math.max(
59 currentSum - smallestSelectedEven + largestUnselectedOdd,
60 currentSum - smallestSelectedOdd + largestUnselectedEven
61 );
62
63 // Return -1 if no valid even sum can be formed
64 return maxEvenSum < 0 ? -1 : maxEvenSum;
65}
66
Time and Space Complexity
The time complexity is O(n Γ log n)
, where n
is the length of the array. This is dominated by the sorting operation nums.sort()
which uses Timsort algorithm with O(n Γ log n)
complexity. The subsequent operations include:
- Computing sum of last
k
elements:O(k)
- First loop through
nums[:n-k]
:O(n-k)
- Second loop through
nums[-k:][::-1]
:O(k)
Since k β€ n
, all these operations are O(n)
, which is dominated by the sorting complexity.
The space complexity is O(log n)
. The sorting algorithm uses O(log n)
space for the recursion stack in the worst case. The slicing operations nums[:n-k]
and nums[-k:][::-1]
create new lists, but in Python's Timsort implementation, the sorting itself requires O(log n)
auxiliary space which dominates. The variables ans
, mx1
, mx2
, mi1
, mi2
use constant O(1)
space.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding Subsequence vs Subset
A critical pitfall is confusing subsequence with subset. The problem states we need a subsequence, which maintains relative order, but the solution sorts the array and picks any k elements. This violates the subsequence constraint.
The Issue: After sorting, we're selecting the largest k elements regardless of their original positions, effectively treating this as a subset problem rather than a subsequence problem.
Example:
- Original array:
[4, 1, 5, 3, 1]
, k = 3 - After sorting:
[1, 1, 3, 4, 5]
- Solution picks:
[3, 4, 5]
(largest 3) - But
[3, 4, 5]
might not be a valid subsequence of the original[4, 1, 5, 3, 1]
2. Edge Case: Insufficient Elements of Required Parity
The algorithm assumes we can always make swaps, but there might be cases where:
- All k selected elements are odd and there are no even elements in the remaining array
- All k selected elements are even and there are no odd elements in the remaining array
Example:
- Array:
[1, 3, 5, 7, 9]
, k = 3 - Selected:
[5, 7, 9]
(sum = 21, odd) - No even numbers available to swap in
- The code would correctly return -1, but the logic might not be immediately clear
3. Incorrect Infinity Handling
When no valid swap candidate exists, the variables remain at infinity/negative infinity, leading to calculations like ans - inf
which results in -inf
. While the code handles this by checking if ans < 0
, this approach is fragile.
Better Solution for Subsequence Problem: If we need to maintain subsequence order, we should use dynamic programming:
def largestEvenSum(self, nums: List[int], k: int) -> int:
n = len(nums)
# dp[i][j][parity] = max sum using first i elements, selecting j elements, with sum parity (0=even, 1=odd)
dp = [[[-1, -1] for _ in range(k + 1)] for _ in range(n + 1)]
dp[0][0][0] = 0 # Base case: 0 elements selected, sum is 0 (even)
for i in range(1, n + 1):
for j in range(k + 1):
# Don't take current element
dp[i][j][0] = dp[i-1][j][0]
dp[i][j][1] = dp[i-1][j][1]
# Take current element if possible
if j > 0:
curr_parity = nums[i-1] % 2
for prev_parity in [0, 1]:
if dp[i-1][j-1][prev_parity] != -1:
new_sum = dp[i-1][j-1][prev_parity] + nums[i-1]
new_parity = (prev_parity + curr_parity) % 2
dp[i][j][new_parity] = max(dp[i][j][new_parity], new_sum)
return dp[n][k][0] # Return max even sum with k elements
4. Optimization Assumption Error
The greedy approach assumes that taking the k largest numbers and then making minimal adjustments will yield the optimal result. However, for subsequences, this isn't necessarily true as we're constrained by order.
Counter-example:
- Array:
[10, 2, 3, 7, 5]
, k = 3 - Greedy after sort might pick
[7, 5, 10]
= 22 (even) - But the valid subsequence
[10, 2, 7]
= 19 (odd) or[10, 3, 5]
= 18 (even) might be the only options
You are given an array of intervals where intervals[i] = [start_i, end_i]
represent the start and end of the ith
interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Donβt Miss This!