Facebook Pixel

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 from nums (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:

  1. Replacing the smallest even number in the selected k with the largest odd number from the remaining elements
  2. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Remove the smallest element from our selection
  2. Add the largest element from the remaining pool
  3. 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.

Learn more about Greedy and Sorting patterns.

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 number
  • mx2: 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 number
  • mi2: 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:

  1. Replace smallest even (mi1) with largest odd (mx1): ans - mi1 + mx1
  2. 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 Evaluator

Example 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
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

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

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More