1383. Maximum Performance of a Team - Explanation

Problem Link



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Sorting with Custom Comparators - Engineers are sorted by efficiency in descending order to fix the minimum efficiency
  • Heap / Priority Queue (Min-Heap) - Used to efficiently maintain the k largest speeds as we iterate
  • Greedy Algorithms - The key insight is fixing the minimum efficiency and maximizing the speed sum
  • Modular Arithmetic - The result must be returned modulo 10^9 + 7, applied only at the end

1. Brute Force (Recursion)

Intuition

The simplest approach is to try all possible subsets of engineers with size at most k. For each subset, we compute the sum of speeds and multiply by the minimum efficiency in that subset. We track the maximum performance across all valid subsets.

This works because performance is defined as (sum of speeds) * (minimum efficiency), so we need to consider every combination to find the optimal team.

Algorithm

  1. Use recursion to explore all choices: for each engineer, decide to either include them in the team or skip them.
  2. Track the current speed sum and the minimum efficiency among selected engineers.
  3. At each step, update the result with the current performance if it improves the maximum.
  4. Base case: when we reach the end of the list or have no more slots (k == 0), stop recursing.
  5. Return the result modulo 109+710^9 + 7.
class Solution:
    def maxPerformance(self, n: int, speed: List[int], efficiency: List[int], k: int) -> int:
        MOD = 1000000007
        res = 0

        def dfs(i, k, speedSum, minEff):
            nonlocal res
            res = max(res, speedSum * minEff)
            if i == n or k == 0:
                return

            dfs(i + 1, k, speedSum, minEff)
            dfs(i + 1, k - 1, speedSum + speed[i], min(minEff, efficiency[i]))

        dfs(0, k, 0, float("inf"))
        return res % MOD

Time & Space Complexity

  • Time complexity: O(2n)O(2 ^ n)
  • Space complexity: O(n)O(n) for recursion stack.

2. Sorting + Min-Heap

Intuition

The key insight is that the minimum efficiency in a team acts as a bottleneck. If we fix which engineer has the minimum efficiency, we want to maximize the sum of speeds among the remaining selected engineers.

By sorting engineers in descending order of efficiency, as we iterate through them, each new engineer we consider has the smallest efficiency so far. At that point, we want to pick the engineers with the highest speeds (up to k total) from those we have seen. A min-heap helps us efficiently maintain the top k speeds.

Algorithm

  1. Pair each engineer's efficiency with their speed and sort by efficiency in descending order.
  2. Use a min-heap to track the speeds of selected engineers.
  3. Iterate through the sorted list. For each engineer:
    • If the heap already has k engineers, remove the one with the smallest speed.
    • Add the current engineer's speed to the heap and update the speed sum.
    • Calculate performance using the current efficiency (which is now the minimum) and update the result.
  4. Return the maximum performance modulo 109+710^9 + 7.
class Solution:
    def maxPerformance(self, n: int, speed: List[int], efficiency: List[int], k: int) -> int:
        MOD = 10**9 + 7
        eng = sorted(zip(efficiency, speed), reverse=True)

        res = speedSum = 0
        minHeap = []

        for eff, spd in eng:
            if len(minHeap) == k:
                speedSum -= heapq.heappop(minHeap)
            speedSum += spd
            heapq.heappush(minHeap, spd)
            res = max(res, eff * speedSum)

        return res % MOD

Time & Space Complexity

  • Time complexity: O(n(logn+logk))O(n * (\log n + \log k))
  • Space complexity: O(n+k)O(n + k)

Where nn is the number of engineers and kk is the maximum number of engineers that can be selected.


Common Pitfalls

Integer Overflow When Computing Performance

The performance value is (sum of speeds) * (minimum efficiency), which can exceed the 32-bit integer range for large inputs. Use 64-bit integers (long in Java, long long in C++) for intermediate calculations, and only apply the modulo when returning the final result.

Applying Modulo During Computation Instead of at the End

The modulo operation should only be applied to the final answer, not during intermediate maximum comparisons. Applying modulo too early (e.g., res = max(res, performance % MOD)) corrupts the comparison logic because a smaller modulo result might incorrectly appear larger than the true maximum.

Misunderstanding "At Most k Engineers"

The problem asks for at most k engineers, meaning teams of size 1, 2, ..., up to k are all valid. A common mistake is only considering teams of exactly size k. The heap-based solution naturally handles this by computing performance at each step as engineers are added.

Incorrect Heap Management

When the heap exceeds k engineers, you must remove the engineer with the smallest speed (the top of a min-heap). A common error is using a max-heap instead, which would remove the fastest engineer and yield suboptimal results. The min-heap ensures we always keep the k fastest engineers seen so far.

Forgetting to Update Speed Sum When Popping from Heap

After removing an engineer from the heap, the speed sum must be decremented by that engineer's speed. Forgetting this update causes the speed sum to be incorrectly inflated, leading to wrong performance calculations.