2542. Maximum Subsequence Score - Explanation
Prerequisites
Before attempting this problem, you should be comfortable with:
- Heap / Priority Queue - Min-heap is used to maintain the k largest elements from nums1 efficiently
- Sorting - Pairs are sorted by nums2 values to process elements in decreasing order of the minimum candidate
- Greedy Algorithms - Understanding when to include/exclude elements to maximize the score
- Two Arrays Coordination - Working with parallel arrays where indices must be processed together
1. Brute Force (Recursion)
Intuition
We need to select exactly k indices. The score is the sum of selected elements from nums1 multiplied by the minimum of selected elements from nums2. Since we must choose exactly k elements, we can use recursion to try all combinations: for each index, either include it or skip it. When we include an element, we update the running sum and track the minimum from nums2.
Algorithm
- Define a recursive function with parameters: current index, remaining count
k, current minimum fromnums2, and current sum fromnums1. - Base case: if
k == 0, returnsum * minVal. - If we cannot select
kmore elements, return negative infinity. - If
minValis0, the result is0(any selection will have score0). - Try both options:
- Skip the current index.
- Include the current index (update min and sum).
- Return the maximum of both choices.
class Solution:
def maxScore(self, nums1: List[int], nums2: List[int], k: int) -> int:
n = len(nums1)
def dfs(i, k, minVal, curSum):
if k == 0:
return curSum * minVal
if i == n or (n - i) < k:
return float("-inf")
if minVal == 0:
return 0
res = dfs(i + 1, k, minVal, curSum)
res = max(res, dfs(i + 1, k - 1, min(minVal, nums2[i]), curSum + nums1[i]))
return res
return dfs(0, k, float("inf"), 0)Time & Space Complexity
- Time complexity:
- Space complexity: for recursion stack.
2. Min-Heap - I
Intuition
The key insight is that if we fix which element provides the minimum from nums2, we want the k largest corresponding values from nums1 (among elements with nums2 values >= our minimum). By sorting pairs by nums2 in descending order and processing them one by one, each new element becomes the new minimum. We maintain the k largest nums1 values seen so far using a min-heap.
Algorithm
- Create pairs of
(nums1[i], nums2[i])and sort bynums2in descending order. - Use a min-heap to track the
klargestnums1values seen so far. - Maintain a running sum of elements in the heap.
- For each pair in sorted order:
- Add the
nums1value to the heap and update the sum. - If heap size exceeds
k, remove the smallest and subtract from sum. - If heap size equals
k, calculate score (sum * current nums2) and update result.
- Add the
- Return the maximum score.
class Solution:
def maxScore(self, nums1: List[int], nums2: List[int], k: int) -> int:
pairs = sorted(zip(nums1, nums2), key=lambda p: p[1], reverse=True)
minHeap = []
n1Sum = 0
res = 0
for n1, n2 in pairs:
n1Sum += n1
heapq.heappush(minHeap, n1)
if len(minHeap) > k:
n1Sum -= heapq.heappop(minHeap)
if len(minHeap) == k:
res = max(res, n1Sum * n2)
return resTime & Space Complexity
- Time complexity:
- Space complexity:
3. Min-Heap - II
Intuition
This is a space-optimized version that packs both values into a single 64-bit integer. Since the problem constraints allow values up to 10^5, we can use bit shifting to combine nums2 (upper bits) and nums1 (lower bits). Sorting these combined values gives us the same ordering as sorting by nums2, and we can extract both values using bitwise operations.
Algorithm
- For each index, create a combined value:
(nums2[i] << 30) | nums1[i]. - Sort the combined array in descending order (sorts by
nums2primarily). - Process each combined value:
- Extract
nums1andnums2using bit operations. - Add
nums1to heap and sum; remove smallest if heap exceedsk. - When heap has exactly
kelements, calculate and track maximum score.
- Extract
- Return the maximum score.
class Solution:
def maxScore(self, nums1: List[int], nums2: List[int], k: int) -> int:
n = len(nums1)
arr = [(nums2[i] << 30) | nums1[i] for i in range(n)]
arr.sort(reverse=True)
minHeap = []
n1Sum = 0
res = 0
for num in arr:
n1, n2 = num & ((1 << 30) - 1), num >> 30
n1Sum += n1
heapq.heappush(minHeap, n1)
if len(minHeap) > k:
n1Sum -= heapq.heappop(minHeap)
if len(minHeap) == k:
res = max(res, n1Sum * n2)
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Common Pitfalls
Using a Max-Heap Instead of a Min-Heap
To maximize the sum of k elements from nums1, you need to keep the k largest values seen so far. This requires a min-heap so you can efficiently remove the smallest element when the heap exceeds size k. Using a max-heap makes it impossible to efficiently evict the smallest element, leading to incorrect tracking of the top k values.
Forgetting to Sort by nums2 in Descending Order
The algorithm relies on processing elements in decreasing order of nums2 values so that each new element becomes the new minimum. If you sort in ascending order or forget to sort entirely, the minimum tracking breaks and the score calculation becomes incorrect.
Not Updating the Sum When Removing from the Heap
When the heap size exceeds k and you pop the smallest element, you must also subtract that element's value from your running sum. Forgetting to update the sum after removal causes it to include values no longer in the heap, producing inflated and incorrect scores.