2542. Maximum Subsequence Score
Problem Description
You have two arrays nums1
and nums2
, both with the same length n
. Your task is to select exactly k
indices from these arrays to maximize a specific score.
The score is calculated as follows:
- Take the sum of all selected elements from
nums1
- Multiply this sum by the minimum value among the corresponding selected elements from
nums2
- Formula:
(nums1[i₀] + nums1[i₁] + ... + nums1[iₖ₋₁]) × min(nums2[i₀], nums2[i₁], ..., nums2[iₖ₋₁])
For example, if you select indices 0, 2, and 4:
- Sum from
nums1
:nums1[0] + nums1[2] + nums1[4]
- Minimum from
nums2
:min(nums2[0], nums2[2], nums2[4])
- Score: sum × minimum
The selected indices form a subsequence, meaning you can pick any k
indices from the arrays (not necessarily consecutive).
Your goal is to find the selection of k
indices that gives the maximum possible score.
The solution approach sorts the pairs (nums2[i], nums1[i])
in descending order by nums2
values. Then it traverses through these sorted pairs, maintaining a min heap of size k
containing elements from nums1
. For each position, when the heap reaches size k
, it calculates the score using the current sum and the current nums2
value (which acts as the minimum since we're traversing in descending order). The heap helps efficiently manage which k
elements from nums1
to keep for the maximum sum at each step.
Intuition
The key insight is that once we fix the minimum value from nums2
, we want to maximize the sum from nums1
. This suggests we should consider each possible minimum value and find the best sum for that minimum.
Think about it this way: if we've already decided that element nums2[i]
will be our minimum, then we know all selected elements from nums2
must be greater than or equal to nums2[i]
. This means we can only choose indices where nums2[j] >= nums2[i]
.
This leads us to a greedy strategy: sort the pairs by nums2
values in descending order. As we traverse from left to right, each nums2
value we encounter could potentially be the minimum in our final selection. At each position i
, all elements to the left have nums2
values greater than or equal to nums2[i]
, making them valid candidates to combine with.
Now the problem becomes: given that nums2[i]
is our minimum, which k-1
elements should we pick from the left (plus the current element) to maximize the sum from nums1
? We want the largest k
elements from nums1
among all valid positions.
A min heap of size k
perfectly solves this subproblem. As we traverse, we:
- Add the current
nums1
value to our heap - If heap size exceeds
k
, remove the smallest element - When heap has exactly
k
elements, calculate the score using the currentnums2
value as the minimum and the heap sum as ournums1
sum
This way, at each position, we maintain the best possible k
elements from nums1
that can be paired with the current minimum from nums2
, ensuring we check all possible minimums and find the optimal score.
Learn more about Greedy, Sorting and Heap (Priority Queue) patterns.
Solution Approach
The implementation follows these steps:
-
Create and Sort Pairs: First, we zip
nums2
andnums1
together to create pairs(nums2[i], nums1[i])
, then sort these pairs in descending order bynums2
values. This ensures as we traverse from left to right, eachnums2
value we encounter could serve as the minimum for our selection.nums = sorted(zip(nums2, nums1), reverse=True)
-
Initialize Data Structures:
q
: A min heap to maintain the largestk
elements fromnums1
s
: Running sum of elements currently in the heapans
: Maximum score found so far
q = [] ans = s = 0
-
Traverse Sorted Pairs: For each pair
(a, b)
wherea
is fromnums2
andb
is fromnums1
:- Add
b
to the running sum and push it to the heap
s += b heappush(q, b)
- Add
-
Maintain Heap Size and Calculate Score:
- When the heap reaches size
k
, we have exactlyk
elements:- Calculate the score:
s * a
(sum ofk
largestnums1
values × current minimuma
) - Update the maximum score
- Remove the smallest element from the heap to make room for potentially better elements in future iterations
- Calculate the score:
if len(q) == k: ans = max(ans, s * a) s -= heappop(q)
- When the heap reaches size
The min heap ensures we always keep the k
largest elements from nums1
seen so far. By processing in descending order of nums2
, we guarantee that when we calculate the score at position i
, the value nums2[i]
is indeed the minimum among all selected indices, and we're pairing it with the optimal sum from nums1
.
Time complexity: O(n log n)
for sorting plus O(n log k)
for heap operations.
Space complexity: O(n)
for the sorted array and O(k)
for the heap.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example with nums1 = [2, 1, 14, 12]
, nums2 = [11, 7, 13, 6]
, and k = 3
.
Step 1: Create and sort pairs by nums2 (descending)
- Original pairs:
(11,2), (7,1), (13,14), (6,12)
- Sorted pairs:
(13,14), (11,2), (7,1), (6,12)
Step 2: Traverse sorted pairs with min heap
Iteration 1: Process (13,14)
- Add 14 to heap:
heap = [14]
,sum = 14
- Heap size (1) < k (3), so no score calculation yet
Iteration 2: Process (11,2)
- Add 2 to heap:
heap = [2, 14]
,sum = 16
- Heap size (2) < k (3), so no score calculation yet
Iteration 3: Process (7,1)
- Add 1 to heap:
heap = [1, 2, 14]
,sum = 17
- Heap size equals k! Calculate score:
17 × 7 = 119
- Update
ans = 119
- Remove smallest (1) from heap:
heap = [2, 14]
,sum = 16
Iteration 4: Process (6,12)
- Add 12 to heap:
heap = [2, 14, 12]
,sum = 28
- Heap size equals k! Calculate score:
28 × 6 = 168
- Update
ans = 168
- Remove smallest (2) from heap:
heap = [12, 14]
,sum = 26
Result: Maximum score is 168, achieved by selecting indices with values (13,14), (6,12), and (11,2) from the original arrays. The sum from nums1 is 14+12+2=28, the minimum from nums2 is min(13,6,11)=6, giving us 28×6=168.
The key insight: when we calculate the score at each position, that position's nums2 value is guaranteed to be the minimum (since we're traversing in descending order), and our heap maintains the k largest nums1 values we've seen so far that can be paired with this minimum.
Solution Implementation
1from typing import List
2import heapq
3
4class Solution:
5 def maxScore(self, nums1: List[int], nums2: List[int], k: int) -> int:
6 # Create pairs of (nums2[i], nums1[i]) and sort by nums2 values in descending order
7 # This ensures we process larger nums2 values first (which could be our minimum)
8 paired_values = sorted(zip(nums2, nums1), reverse=True)
9
10 # Min heap to track the k smallest nums1 values we've selected
11 min_heap = []
12
13 # Initialize maximum score and current sum of selected nums1 values
14 max_score = 0
15 current_sum = 0
16
17 # Iterate through pairs in descending order of nums2 values
18 for nums2_value, nums1_value in paired_values:
19 # Add current nums1 value to our selection
20 current_sum += nums1_value
21 heapq.heappush(min_heap, nums1_value)
22
23 # When we have exactly k elements
24 if len(min_heap) == k:
25 # Calculate score: sum of k nums1 values * minimum nums2 value
26 # nums2_value is the minimum because we sorted in descending order
27 max_score = max(max_score, current_sum * nums2_value)
28
29 # Remove the smallest nums1 value to make room for potentially better values
30 # This maintains a sliding window of k elements
31 current_sum -= heapq.heappop(min_heap)
32
33 return max_score
34
1class Solution {
2 public long maxScore(int[] nums1, int[] nums2, int k) {
3 int n = nums1.length;
4
5 // Create pairs of (nums1[i], nums2[i]) for easier manipulation
6 int[][] pairs = new int[n][2];
7 for (int i = 0; i < n; i++) {
8 pairs[i] = new int[] {nums1[i], nums2[i]};
9 }
10
11 // Sort pairs by nums2 values in descending order
12 // This ensures we process larger nums2 values first
13 Arrays.sort(pairs, (a, b) -> b[1] - a[1]);
14
15 long maxResult = 0;
16 long currentSum = 0;
17
18 // Min heap to maintain k largest nums1 values
19 // Automatically removes the smallest when size exceeds k
20 PriorityQueue<Integer> minHeap = new PriorityQueue<>();
21
22 // Iterate through sorted pairs
23 for (int i = 0; i < n; i++) {
24 // Add current nums1 value to sum and heap
25 currentSum += pairs[i][0];
26 minHeap.offer(pairs[i][0]);
27
28 // When we have exactly k elements, calculate score
29 if (minHeap.size() == k) {
30 // Score = sum of k nums1 values * minimum nums2 value
31 // Since pairs are sorted by nums2 descending, pairs[i][1] is the minimum
32 maxResult = Math.max(maxResult, currentSum * pairs[i][1]);
33
34 // Remove smallest nums1 value to make room for next iteration
35 currentSum -= minHeap.poll();
36 }
37 }
38
39 return maxResult;
40 }
41}
42
1class Solution {
2public:
3 long long maxScore(vector<int>& nums1, vector<int>& nums2, int k) {
4 int n = nums1.size();
5
6 // Create pairs of (nums2[i], nums1[i]) and store negative of nums2[i] for sorting
7 // We negate nums2[i] to sort in descending order of nums2 values
8 vector<pair<int, int>> pairs(n);
9 for (int i = 0; i < n; ++i) {
10 pairs[i] = {-nums2[i], nums1[i]};
11 }
12
13 // Sort pairs by nums2 values in descending order (due to negation)
14 sort(pairs.begin(), pairs.end());
15
16 // Min heap to maintain k largest elements from nums1
17 priority_queue<int, vector<int>, greater<int>> minHeap;
18
19 long long maxResult = 0;
20 long long currentSum = 0;
21
22 // Iterate through pairs in descending order of nums2 values
23 for (auto& [negatedNum2Value, num1Value] : pairs) {
24 // Add current nums1 value to sum and heap
25 currentSum += num1Value;
26 minHeap.push(num1Value);
27
28 // When we have exactly k elements, calculate score
29 if (minHeap.size() == k) {
30 // Score = sum of k nums1 values * minimum nums2 value among selected
31 // negatedNum2Value is negative, so we negate it back
32 maxResult = max(maxResult, currentSum * (-negatedNum2Value));
33
34 // Remove smallest nums1 value to make room for next iteration
35 currentSum -= minHeap.top();
36 minHeap.pop();
37 }
38 }
39
40 return maxResult;
41 }
42};
43
1function maxScore(nums1: number[], nums2: number[], k: number): number {
2 const n = nums1.length;
3
4 // Create pairs of [nums2[i], nums1[i]] and sort by nums2 in descending order
5 const pairs: [number, number][] = [];
6 for (let i = 0; i < n; i++) {
7 pairs.push([nums2[i], nums1[i]]);
8 }
9
10 // Sort pairs by nums2 values in descending order
11 pairs.sort((a, b) => b[0] - a[0]);
12
13 // Min heap to maintain k largest elements from nums1
14 // Using array with manual heap operations since TypeScript doesn't have built-in heap
15 const minHeap: number[] = [];
16
17 // Helper function to maintain min heap property after insertion
18 const heapifyUp = (index: number): void => {
19 while (index > 0) {
20 const parentIndex = Math.floor((index - 1) / 2);
21 if (minHeap[parentIndex] > minHeap[index]) {
22 [minHeap[parentIndex], minHeap[index]] = [minHeap[index], minHeap[parentIndex]];
23 index = parentIndex;
24 } else {
25 break;
26 }
27 }
28 };
29
30 // Helper function to maintain min heap property after removal
31 const heapifyDown = (index: number): void => {
32 while (true) {
33 const leftChild = 2 * index + 1;
34 const rightChild = 2 * index + 2;
35 let smallest = index;
36
37 if (leftChild < minHeap.length && minHeap[leftChild] < minHeap[smallest]) {
38 smallest = leftChild;
39 }
40 if (rightChild < minHeap.length && minHeap[rightChild] < minHeap[smallest]) {
41 smallest = rightChild;
42 }
43
44 if (smallest !== index) {
45 [minHeap[smallest], minHeap[index]] = [minHeap[index], minHeap[smallest]];
46 index = smallest;
47 } else {
48 break;
49 }
50 }
51 };
52
53 // Helper function to add element to min heap
54 const heapPush = (value: number): void => {
55 minHeap.push(value);
56 heapifyUp(minHeap.length - 1);
57 };
58
59 // Helper function to remove minimum element from heap
60 const heapPop = (): number => {
61 if (minHeap.length === 0) return 0;
62
63 const minValue = minHeap[0];
64 minHeap[0] = minHeap[minHeap.length - 1];
65 minHeap.pop();
66
67 if (minHeap.length > 0) {
68 heapifyDown(0);
69 }
70
71 return minValue;
72 };
73
74 let maxResult = 0;
75 let currentSum = 0;
76
77 // Iterate through pairs in descending order of nums2 values
78 for (const [nums2Value, nums1Value] of pairs) {
79 // Add current nums1 value to sum and heap
80 currentSum += nums1Value;
81 heapPush(nums1Value);
82
83 // When we have exactly k elements, calculate score
84 if (minHeap.length === k) {
85 // Score = sum of k nums1 values * minimum nums2 value among selected
86 // Since we're iterating in descending order, current nums2Value is the minimum
87 maxResult = Math.max(maxResult, currentSum * nums2Value);
88
89 // Remove smallest nums1 value to make room for next iteration
90 const minValue = heapPop();
91 currentSum -= minValue;
92 }
93 }
94
95 return maxResult;
96}
97
Time and Space Complexity
Time Complexity: O(n × log n)
The time complexity is dominated by two main operations:
- Sorting the zipped pairs
(nums2[i], nums1[i])
in descending order takesO(n × log n)
time, wheren
is the length of the input arrays. - The for loop iterates through all
n
elements, and within each iteration:heappush(q, b)
takesO(log k)
time (heap size is at mostk
)heappop(q)
takesO(log k)
time when the heap size reachesk
Since k ≤ n
, the loop operations contribute O(n × log k)
which is bounded by O(n × log n)
. Therefore, the overall time complexity is O(n × log n)
.
Space Complexity: O(n)
The space complexity consists of:
- The
nums
list storing the zipped and sorted pairs requiresO(n)
space. - The min-heap
q
stores at mostk
elements, requiringO(k)
space. - Other variables (
ans
,s
,a
,b
) useO(1)
space.
Since k ≤ n
, the total space complexity is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Heap Type Selection
Pitfall: Using a max heap instead of a min heap, or misunderstanding why we need a min heap.
Many developers intuitively think "we want maximum values, so let's use a max heap." However, we actually need a min heap to efficiently remove the smallest element when our heap exceeds size k.
Why this happens: When maintaining the k largest elements, we need to remove the smallest among them when adding a new element. A min heap gives us O(1) access to the smallest element.
Solution:
# Correct: Use min heap to maintain k largest elements min_heap = [] heapq.heappush(min_heap, value) # Smallest element at root # Incorrect: Trying to use max heap (Python doesn't have built-in max heap) # This would require negating values, adding complexity
2. Forgetting to Update Sum When Popping from Heap
Pitfall: Only popping from the heap without adjusting the running sum accordingly.
# Incorrect - sum becomes inaccurate
if len(min_heap) == k:
max_score = max(max_score, current_sum * nums2_value)
heapq.heappop(min_heap) # Forgot to update current_sum!
# Correct - maintain accurate sum
if len(min_heap) == k:
max_score = max(max_score, current_sum * nums2_value)
current_sum -= heapq.heappop(min_heap) # Update sum when removing
3. Calculating Score Before Having k Elements
Pitfall: Computing the score when heap size is less than k, which violates the problem constraint of selecting exactly k indices.
# Incorrect - calculates score even with fewer than k elements
for nums2_value, nums1_value in paired_values:
current_sum += nums1_value
heapq.heappush(min_heap, nums1_value)
max_score = max(max_score, current_sum * nums2_value) # Wrong!
# Correct - only calculate when we have exactly k elements
for nums2_value, nums1_value in paired_values:
current_sum += nums1_value
heapq.heappush(min_heap, nums1_value)
if len(min_heap) == k: # Check size first
max_score = max(max_score, current_sum * nums2_value)
current_sum -= heapq.heappop(min_heap)
4. Incorrect Pairing Order
Pitfall: Pairing the arrays incorrectly or sorting by the wrong array.
# Incorrect - sorting by nums1 instead of nums2
paired_values = sorted(zip(nums1, nums2), reverse=True)
# Correct - sort by nums2 (which will be our minimum)
paired_values = sorted(zip(nums2, nums1), reverse=True)
5. Edge Case: When n < k
Pitfall: Not considering that the arrays might have fewer elements than k.
Solution: The algorithm naturally handles this case because the heap never reaches size k, so no score is calculated and the default value of 0 is returned. However, it's worth being aware of this edge case for debugging purposes.
Which of the tree traversal order can be used to obtain elements in a binary search tree in sorted order?
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
https assets algo monster cover_photos heap svg Priority Queue and Heap What is the relationship between priority queue and heap Priority Queue is an Abstract Data Type and Heap is the concrete data structure we use to implement a priority queue Priority Queue A priority queue is a data structure
Want a Structured Path to Master System Design Too? Don’t Miss This!