1383. Maximum Performance of a Team
Problem Description
In this problem, we are given two integer values n
(the number of engineers) and k
(the maximum number of engineers we can select), along with two integer arrays speed
and efficiency
, each of length n
. The speed
array represents the speed of each engineer, while the efficiency
array represents the efficiency of each engineer.
Our task is to form a team with at most k
engineers to achieve the maximum performance. The performance of a team is defined as the sum of the speeds of the selected engineers multiplied by the minimum efficiency among them.
We need to calculate the maximum possible performance of a team formed under these constraints and return this maximum value modulo 10^9 + 7
to handle very large numbers.
Intuition
To achieve the maximum performance, we need to consider both speed and efficiency in our selection strategy. A greedy approach can help us select the most suitable candidates.
One key insight is that if we pick an engineer with a certain efficiency, then the total efficiency of the team cannot exceed this value. Therefore, to maximize the team's performance at each step, we aim to add engineers with the highest speeds possible without dropping the minimum efficiency of the team below the current candidate's efficiency.
Here's how we arrive at the solution approach:
-
We pair the speed and efficiency for each engineer and then sort these pairs in descending order of efficiency. This ensures that as we iterate through the engineers, we maintain the invariant that we only consider teams where the minimum efficiency is at least as much as the current engineer's efficiency.
-
We use a min-heap (which in Python is conveniently implemented through the
heapq
module) to keep track of the smallest speeds in our current team selection. This is useful because, if the team has more thank
engineers, we will need to remove the one with the smallest speed to add a new one. -
As we iterate through the sorted engineer list, we do the following:
- Add the current engineer's speed to the total speed (
tot
). - Calculate the current performance by multiplying the total speed with the engineer's efficiency.
- Check if this current performance is greater than the maximum performance found so far and update
ans
if it is. - Add the current engineer's speed to the heap.
- If the heap's size exceeds
k
, remove the smallest speed from it and adjust the total speed (tot
) accordingly.
- Add the current engineer's speed to the total speed (
-
The largest value found during this process is our maximum performance. Because large numbers are involved, we return the result modulo
10^9 + 7
.
This approach ensures that at any point, the team is formed by choosing engineers that contribute maximally to the speed while being limited by the engineer with the lowest efficiency in the team.
Learn more about Greedy, Sorting and Heap (Priority Queue) patterns.
Solution Approach
The solution makes good use of both sorting and a min-heap to achieve the desired outcome. Here's a step-by-step explanation of the implementation:
-
Sorting by Efficiency: We start by creating a list of tuples
t
that pairs each engineer'sspeed
with their correspondingefficiency
. We then sort this list in descending order based onefficiency
. This allows us to process the engineers in order of decreasing efficiency, so at each step, we consider the highest possible efficiency for the team and try to maximize speed.t = sorted(zip(speed, efficiency), key=lambda x: -x[1])
-
Initial Declarations: We initialize three variables:
ans
to track the maximum performance encountered so far.tot
to keep the running sum of the speeds of the engineers in the current team.mod
to store the modulus value of10^9 + 7
for use at the end of the calculation.
ans = tot = 0 mod = 10**9 + 7
-
Min-Heap Usage: We declare a list
h
which will serve as our min-heap using theheapq
library. This is where we will store the speeds of the engineers we choose for our team. A min-heap allows us to efficiently retrieve and remove the engineer with the smallest speed when needed.h = []
-
Iterating over Engineers: We now iterate over each engineer in the sorted list
t
. For each engineer, we perform the following steps:- Add the engineer's speed to
tot
. - Calculate a potential maximum performance
tot * e
and compare this withans
, updatingans
if it's larger. This step considers that the current engineer's efficiency sets the new minimum efficiency for the team. - Add the engineer's speed to the min-heap.
- If the heap size reaches
k
, we remove the smallest speed usingheappop
, which would be the one at the root of the min-heap, and subtract that value fromtot
. This step ensures that the team size does not exceedk
.
for s, e in t: tot += s ans = max(ans, tot * e) heappush(h, s) if len(h) == k: tot -= heappop(h)
- Add the engineer's speed to
-
Modulo Operation: After the loop is done,
ans
holds the maximum performance value. We returnans % mod
to ensure the result adheres to the modulo constraint.return ans % mod
The use of sorting ensures that we're always considering engineers in the order of their efficiency, from highest to lowest, which is crucial for maximizing performance. The min-heap is essential for managing the team size and quickly ejecting the least-contributing member (in terms of speed) when the team exceeds the size k
. With this strategy, the complexity of the solution is dictated by the sorting step, which is O(n log n)
, and the heap operations, which have O(log k)
complexity for each of the n
engineers, making the overall complexity O(n log n + n log k)
.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's illustrate the solution approach with a small example:
- Suppose we have
n = 4
engineers andk = 2
as the maximum number of engineers we can select. - Let's assume the
speed
array is[5, 2, 3, 9]
and theefficiency
array is[10, 1, 4, 7]
.
Following the steps described in the solution approach:
-
Sorting by Efficiency: We first pair each engineer's speed with their corresponding efficiency and sort the data by efficiency in descending order.
Pairs formed:
[(5, 10), (2, 1), (3, 4), (9, 7)]
After sorting by efficiency:
[(5, 10), (9, 7), (3, 4), (2, 1)]
-
Initial Declarations:
Variable initialization:
ans
= 0tot
= 0mod
=10^9 + 7
-
Min-Heap Usage:
Initialize an empty min-heap
h
. -
Iterating over Engineers: Now we consider each engineer in
t
.- For engineer (5, 10):
tot
becomes5
.- Potential performance:
5 * 10
=50
.ans
is updated to50
. - Add speed
5
to the heap. Heaph
:[5]
.
- For engineer (9, 7):
tot
becomes14
(5
+9
).- Potential performance:
14 * 7
=98
.ans
is updated to98
. - Add speed
9
to the heap. Heaph
:[5, 9]
.
- For engineer (3, 4):
tot
becomes17
(14
+3
).- Potential performance:
17 * 4
=68
, which is less thanans
(98
), so no update is needed. - Add speed
3
to the heap. Heaph
:[3, 9, 5]
. - Since the heap now exceeds
k
(size is3
), we remove the smallest speed3
(heap pop).tot
becomes14
(17
-3
).
- For engineer (2, 1):
tot
becomes16
(14
+2
).- Potential performance:
16 * 1
=16
, which is less thanans
(98
), so no update is needed. - Add speed
2
to the heap. Heaph
:[2, 9, 5]
. - Again, since the heap size exceeds
k
, we remove the smallest speed2
(heap pop).tot
becomes14
(16
-2
).
The loop ends and the largest value found during this process is
ans
=98
. - For engineer (5, 10):
-
Modulo Operation:
Finally, we return
ans % mod
, which is98 % (10^9 + 7) = 98
because98
is already less than10^9 + 7
.
By the end of this process, we've determined that the maximum performance that can be achieved with a team of at most k
engineers is 98
.
Solution Implementation
1from typing import List
2from heapq import heappush, heappop
3
4class Solution:
5 def maxPerformance(self, n: int, speed: List[int], efficiency: List[int], k: int) -> int:
6 # Combine the speed and efficiency into a single list of tuples
7 # and sort it in descending order of efficiency.
8 combined = sorted(zip(speed, efficiency), key=lambda x: -x[1])
9
10 max_performance = total_speed = 0
11 mod = 10**9 + 7
12 min_speed_heap = [] # A min-heap to track the smallest speeds.
13
14 # Iterate through the engineers sorted by efficiency.
15 for curr_speed, curr_efficiency in combined:
16 # Add the current engineer's speed to the total speed.
17 total_speed += curr_speed
18
19 # Calculate the current performance with the current engineer.
20 max_performance = max(max_performance, total_speed * curr_efficiency)
21
22 # Add the current speed to the min heap.
23 heappush(min_speed_heap, curr_speed)
24
25 # If the size of the team exceeds k, remove the engineer with the smallest speed.
26 if len(min_speed_heap) > k:
27 # Remove the smallest speed from the total speed as we pop from the heap.
28 total_speed -= heappop(min_speed_heap)
29
30 # The result could be large; we return the result modulo 10^9 + 7.
31 return max_performance % mod
32
1class Solution {
2 private static final int MODULUS = (int) 1e9 + 7; // Define a constant for the modulus value
3
4 public int maxPerformance(int n, int[] speed, int[] efficiency, int k) {
5 // Create an array to store speed and efficiency pairs
6 int[][] engineers = new int[n][2];
7 for (int i = 0; i < n; ++i) {
8 engineers[i] = new int[] {speed[i], efficiency[i]};
9 }
10
11 // Sort the engineers array by efficiency in descending order
12 Arrays.sort(engineers, (a, b) -> b[1] - a[1]);
13
14 // Use a min heap to keep track of the k highest speeds
15 PriorityQueue<Integer> speedQueue = new PriorityQueue<>();
16
17 long totalSpeed = 0; // Total speed of the selected engineers
18 long maxPerformance = 0; // Max performance found so far
19
20 // Iterate through the sorted engineers
21 for (var engineer : engineers) {
22 int currentSpeed = engineer[0];
23 int currentEfficiency = engineer[1];
24
25 // Add the current speed to the total and update the max performance
26 totalSpeed += currentSpeed;
27 maxPerformance = Math.max(maxPerformance, totalSpeed * currentEfficiency);
28
29 // Add the current speed to the speed queue
30 speedQueue.offer(currentSpeed);
31
32 // If the size of the speedQueue exceeds k, remove the slowest engineer
33 if (speedQueue.size() > k) {
34 // Polling removes the smallest element (min heap property)
35 totalSpeed -= speedQueue.poll();
36 }
37 }
38
39 // Return the max performance modulo the defined modulus to prevent overflow
40 return (int) (maxPerformance % MODULUS);
41 }
42}
43
1#include <vector>
2#include <queue>
3#include <algorithm>
4
5using namespace std;
6
7class Solution {
8public:
9 int maxPerformance(int numEngineers, vector<int>& speeds, vector<int>& efficiencies, int maxTeamSize) {
10 // Pair speed with efficiency, and sort engineers by increasing efficiency
11 vector<pair<int, int>> engineerPair(numEngineers);
12 for (int i = 0; i < numEngineers; ++i) {
13 engineerPair[i] = {efficiencies[i], speeds[i]};
14 }
15 // Sort the engineers primarily by efficiency in descending order
16 sort(engineerPair.rbegin(), engineerPair.rend());
17
18 // Min heap to keep track of the lowest speed in the current team for easy removal
19 priority_queue<int, vector<int>, greater<int>> speedHeap;
20 long long maxPerformance = 0, totalSpeed = 0;
21 int modulus = 1e9 + 7; // For taking modulo after calculations to prevent overflow
22
23 for (auto& engineer : engineerPair) {
24 int currentSpeed = engineer.second;
25 int currentEfficiency = engineer.first;
26
27 // Add the current engineer's speed to the total speed
28 totalSpeed += currentSpeed;
29
30 // Update max performance with the new team configuration
31 // Here, the performance is calculated as the product of total speed and
32 // the efficiency of the least efficient engineer in the team
33 maxPerformance = max(maxPerformance, totalSpeed * currentEfficiency);
34
35 // Add the current speed to the min heap
36 speedHeap.push(currentSpeed);
37
38 // If the team size exceeds the max allowed size, remove the engineer
39 // with the lowest speed from the team
40 if (speedHeap.size() > maxTeamSize) {
41 totalSpeed -= speedHeap.top(); // Subtract the lowest speed
42 speedHeap.pop(); // Remove the lowest speed
43 }
44 }
45
46 // Return the maximum performance modulo 1e9+7
47 return static_cast<int>(maxPerformance % modulus);
48 }
49};
50
1type Engineer = {
2 speed: number;
3 efficiency: number;
4};
5
6function maxPerformance(numEngineers: number, speeds: number[], efficiencies: number[], maxTeamSize: number): number {
7 // Create an array of objects containing speed and efficiency
8 const engineers: Engineer[] = speeds.map((speed, index) => ({
9 speed: speed,
10 efficiency: efficiencies[index],
11 }));
12
13 // Sort engineers primarily by efficiency in descending order
14 engineers.sort((a, b) => b.efficiency - a.efficiency);
15
16 // Min heap to keep track of the lowest speed in the current team for easy removal
17 const speedHeap: number[] = [];
18 let maxPerformance: number = 0;
19 let totalSpeed: number = 0;
20 const modulus: number = 1e9 + 7; // For taking modulo after calculations to prevent overflow
21
22 engineers.forEach(engineer => {
23 totalSpeed += engineer.speed;
24 // Calculate the maximum performance with the new team configuration
25 maxPerformance = Math.max(maxPerformance, totalSpeed * engineer.efficiency);
26
27 // Add current speed to the heap
28 speedHeap.push(engineer.speed);
29 speedHeap.sort((a, b) => a - b); // Convert array to a min heap by sorting in ascending order
30
31 // If the team size exceeds the max allowed size, remove the engineer with the lowest speed
32 if (speedHeap.length > maxTeamSize) {
33 totalSpeed -= speedHeap.shift() as number; // Shift operation removes the first element, which is the smallest due to min-heap property
34 }
35 });
36
37 return maxPerformance % modulus;
38}
39
Time and Space Complexity
Time Complexity
The given Python code sorts an array of tuples based on the efficiency, uses a min-heap for maintaining the k highest speeds, and iterates through the sorted list to calculate the answer.
- Sorting the array of tuples based on efficiency has a time complexity of
O(n log n)
wheren
is the number of engineers. - Iterating through the sorted array to calculate the maximum performance occurs in
O(n)
, as each element is visited exactly once. - For every engineer processed inside the loop, it may add an element to the min-heap, which is
O(log k)
wherek
is the maximum team size. However, this operation will only happen up tok
times because once the heap size reachesk
, engineers are popped out for every new engineer added. - The 'heappop' operation, which occurs when the heap reaches the size
k
, is alsoO(log k)
.
Therefore, the total time complexity combines the sorting and the heap operations: O(n log n) + O(n log k)
. Since k
can be at most n
, in the worst case, it simplifies to O(n log n)
.
Space Complexity
The space complexity should account for the sorted array of tuples, and the min-heap used to store up to k
elements:
- The sorted array of tuples has a space complexity of
O(n)
, as it storesn
pairs of(speed, efficiency)
. - The min-heap also has a space complexity of
O(k)
as it may store up tok
elements at a time.
The total space complexity is the higher of the two: O(n)
when n
is greater than k
, otherwise O(k)
. In asymptotic notation, we express this as O(max(n, k))
, but since k <= n
, it simplifies to O(n)
.
Learn more about how to find time and space complexity quickly using problem constraints.
Given a sorted array of integers and an integer called target, find the element that
equals to the target and return its index. Select the correct code that fills the
___
in the given code snippet.
1def binary_search(arr, target):
2 left, right = 0, len(arr) - 1
3 while left ___ right:
4 mid = (left + right) // 2
5 if arr[mid] == target:
6 return mid
7 if arr[mid] < target:
8 ___ = mid + 1
9 else:
10 ___ = mid - 1
11 return -1
12
1public static int binarySearch(int[] arr, int target) {
2 int left = 0;
3 int right = arr.length - 1;
4
5 while (left ___ right) {
6 int mid = left + (right - left) / 2;
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
1function binarySearch(arr, target) {
2 let left = 0;
3 let right = arr.length - 1;
4
5 while (left ___ right) {
6 let mid = left + Math.trunc((right - left) / 2);
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
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 algomonster s3 us east 2 amazonaws com 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
Want a Structured Path to Master System Design Too? Don’t Miss This!