Bird
Raised Fist0
Interview Preptwo-heapshardAmazonGoogle

Maximum Performance of a Team

Choose your preparation mode4 modes available
🎯
Maximum Performance of a Team
hardHEAPAmazonGoogle

Imagine you're a manager selecting a team of engineers to maximize overall project performance, balancing their speed and efficiency under a team size limit.

💡 This problem combines sorting and heap data structures to optimize a team selection based on two attributes per member. Beginners often struggle because it requires understanding how to balance multiple constraints and use heaps to maintain a running optimal subset efficiently.
📋
Problem Statement

You are given two integer arrays speed and efficiency, both of length n, and an integer k. You want to form a team of at most k engineers to maximize the team's performance. The performance of a team is defined as the sum of the speeds of the team members multiplied by the minimum efficiency among them. Return the maximum performance of this team modulo 10^9 + 7. Input: - n: integer, number of engineers - speed: integer array of length n - efficiency: integer array of length n - k: integer, maximum team size Output: - integer, maximum performance modulo 10^9 + 7

1 ≤ n ≤ 10^5speed.length == nefficiency.length == n1 ≤ speed[i], efficiency[i] ≤ 10^51 ≤ k ≤ n
💡
Example
Input"n = 6, speed = [2,10,3,1,5,8], efficiency = [5,4,3,9,7,2], k = 2"
Output60

Choose engineers with speed 10 and 5, efficiencies 4 and 7. Minimum efficiency is 4, sum speed is 15, performance = 15*4=60.

  • k = 1 (only one engineer can be chosen) → output is max speed[i]*efficiency[i]
  • All efficiencies are equal → output is sum of top k speeds * that efficiency
  • All speeds are equal → output is speed * minimum efficiency among chosen k
  • Large n with k = n → output is sum of all speeds * minimum efficiency overall
⚠️
Common Mistakes
Not sorting by efficiency descending

Incorrect minimum efficiency assumption leads to wrong performance calculation

✅ Always sort engineers by efficiency descending before processing

Using max-heap instead of min-heap for speeds

Cannot efficiently remove the smallest speed when team size exceeds k

✅ Use a min-heap to remove the smallest speed to maximize sum speed

Not updating sum_speed when removing from heap

Performance calculation becomes incorrect due to stale sum_speed

✅ Subtract removed speed from sum_speed immediately after popping from heap

Forgetting modulo operation on final result

Output may exceed integer limits or problem constraints

✅ Return result modulo 10^9 + 7 as specified

🧠
Brute Force (Check All Teams)
💡 This approach exists to understand the problem's combinatorial nature and why naive enumeration is infeasible, motivating the need for optimization.

Intuition

Try every possible team combination of size up to k, calculate performance, and track the maximum. This brute force method ensures correctness but is too slow for large inputs.

Algorithm

  1. Generate all subsets of engineers with size ≤ k.
  2. For each subset, calculate sum of speeds and minimum efficiency.
  3. Calculate performance = sum_speeds * min_efficiency.
  4. Track and return the maximum performance found.
💡 The challenge is the exponential number of subsets, which makes this approach impractical but conceptually straightforward.
</>
Code
from itertools import combinations

MOD = 10**9 + 7

def maxPerformance(n, speed, efficiency, k):
    max_perf = 0
    for team_size in range(1, k+1):
        for team in combinations(range(n), team_size):
            sum_speed = sum(speed[i] for i in team)
            min_eff = min(efficiency[i] for i in team)
            perf = sum_speed * min_eff
            if perf > max_perf:
                max_perf = perf
    return max_perf % MOD

# Example usage
if __name__ == '__main__':
    n = 6
    speed = [2,10,3,1,5,8]
    efficiency = [5,4,3,9,7,2]
    k = 2
    print(maxPerformance(n, speed, efficiency, k))
Line Notes
from itertools import combinationsImport combinations to generate all possible teams
for team_size in range(1, k+1):Iterate over all possible team sizes up to k to cover all team sizes
for team in combinations(range(n), team_size):Generate all subsets of the current team size
sum_speed = sum(speed[i] for i in team)Calculate total speed for the current team
min_eff = min(efficiency[i] for i in team)Find minimum efficiency in the current team, which is the bottleneck
perf = sum_speed * min_effCalculate performance for the current team
if perf > max_perf:Update max performance if current performance is better
return max_perf % MODReturn the maximum performance modulo 10^9+7 as required
import java.util.*;

public class Solution {
    static final int MOD = 1000000007;

    public static int maxPerformance(int n, int[] speed, int[] efficiency, int k) {
        int maxPerf = 0;
        List<Integer> indices = new ArrayList<>();
        for (int i = 0; i < n; i++) indices.add(i);
        for (int teamSize = 1; teamSize <= k; teamSize++) {
            List<List<Integer>> combos = combinations(indices, teamSize);
            for (List<Integer> team : combos) {
                int sumSpeed = 0;
                int minEff = Integer.MAX_VALUE;
                for (int idx : team) {
                    sumSpeed += speed[idx];
                    minEff = Math.min(minEff, efficiency[idx]);
                }
                int perf = sumSpeed * minEff;
                if (perf > maxPerf) maxPerf = perf;
            }
        }
        return maxPerf % MOD;
    }

    private static List<List<Integer>> combinations(List<Integer> arr, int k) {
        List<List<Integer>> res = new ArrayList<>();
        backtrack(arr, k, 0, new ArrayList<>(), res);
        return res;
    }

    private static void backtrack(List<Integer> arr, int k, int start, List<Integer> temp, List<List<Integer>> res) {
        if (temp.size() == k) {
            res.add(new ArrayList<>(temp));
            return;
        }
        for (int i = start; i < arr.size(); i++) {
            temp.add(arr.get(i));
            backtrack(arr, k, i + 1, temp, res);
            temp.remove(temp.size() - 1);
        }
    }

    public static void main(String[] args) {
        int n = 6;
        int[] speed = {2,10,3,1,5,8};
        int[] efficiency = {5,4,3,9,7,2};
        int k = 2;
        System.out.println(maxPerformance(n, speed, efficiency, k));
    }
}
Line Notes
for (int teamSize = 1; teamSize <= k; teamSize++)Iterate over all team sizes up to k to cover all possibilities
List<List<Integer>> combos = combinations(indices, teamSize);Generate all combinations of the current team size
for (int idx : team)Calculate sum of speeds and minimum efficiency for the current team
if (perf > maxPerf) maxPerf = perf;Update max performance if current is better
return maxPerf % MOD;Return the maximum performance modulo 10^9+7 as required
private static List<List<Integer>> combinations(List<Integer> arr, int k)Helper method to generate all combinations of size k
backtrack(...)Backtracking method to build combinations recursively
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int MOD = 1e9 + 7;

void backtrack(vector<int>& arr, int k, int start, vector<int>& temp, vector<vector<int>>& res) {
    if ((int)temp.size() == k) {
        res.push_back(temp);
        return;
    }
    for (int i = start; i < (int)arr.size(); i++) {
        temp.push_back(arr[i]);
        backtrack(arr, k, i + 1, temp, res);
        temp.pop_back();
    }
}

vector<vector<int>> combinations(vector<int>& arr, int k) {
    vector<vector<int>> res;
    vector<int> temp;
    backtrack(arr, k, 0, temp, res);
    return res;
}

int maxPerformance(int n, vector<int>& speed, vector<int>& efficiency, int k) {
    int maxPerf = 0;
    vector<int> indices(n);
    for (int i = 0; i < n; i++) indices[i] = i;
    for (int teamSize = 1; teamSize <= k; teamSize++) {
        vector<vector<int>> combos = combinations(indices, teamSize);
        for (auto& team : combos) {
            int sumSpeed = 0;
            int minEff = INT_MAX;
            for (int idx : team) {
                sumSpeed += speed[idx];
                minEff = min(minEff, efficiency[idx]);
            }
            int perf = sumSpeed * minEff;
            if (perf > maxPerf) maxPerf = perf;
        }
    }
    return maxPerf % MOD;
}

int main() {
    int n = 6;
    vector<int> speed = {2,10,3,1,5,8};
    vector<int> efficiency = {5,4,3,9,7,2};
    int k = 2;
    cout << maxPerformance(n, speed, efficiency, k) << endl;
    return 0;
}
Line Notes
for (int teamSize = 1; teamSize <= k; teamSize++)Loop over all team sizes up to k to cover all possibilities
vector<vector<int>> combos = combinations(indices, teamSize);Generate all combinations of the current team size
for (int idx : team)Calculate sum of speeds and minimum efficiency for the current team
if (perf > maxPerf) maxPerf = perf;Update max performance if current is better
return maxPerf % MOD;Return the maximum performance modulo 10^9+7 as required
void backtrack(...)Recursive helper to generate combinations by backtracking
temp.push_back(arr[i])Add current element to the temporary combination
temp.pop_back()Remove last element to backtrack and try other combinations
function* combinations(arr, k, start = 0, combo = []) {
    if (combo.length === k) {
        yield combo;
        return;
    }
    for (let i = start; i < arr.length; i++) {
        yield* combinations(arr, k, i + 1, combo.concat(arr[i]));
    }
}

const MOD = 1e9 + 7;

function maxPerformance(n, speed, efficiency, k) {
    let maxPerf = 0;
    const indices = [...Array(n).keys()];
    for (let teamSize = 1; teamSize <= k; teamSize++) {
        for (const team of combinations(indices, teamSize)) {
            let sumSpeed = 0;
            let minEff = Infinity;
            for (const idx of team) {
                sumSpeed += speed[idx];
                minEff = Math.min(minEff, efficiency[idx]);
            }
            const perf = sumSpeed * minEff;
            if (perf > maxPerf) maxPerf = perf;
        }
    }
    return maxPerf % MOD;
}

// Example usage
console.log(maxPerformance(6, [2,10,3,1,5,8], [5,4,3,9,7,2], 2));
Line Notes
function* combinations(arr, k, start = 0, combo = [])Generator function to yield all combinations of size k recursively
if (combo.length === k)Base case: yield the current combination when size k is reached
for (let i = start; i < arr.length; i++)Iterate over remaining elements to build combinations
yield* combinations(arr, k, i + 1, combo.concat(arr[i]))Recursively yield combinations including current element
for (let teamSize = 1; teamSize <= k; teamSize++)Iterate over all team sizes up to k
for (const idx of team)Calculate sum speed and min efficiency for current team
if (perf > maxPerf) maxPerf = perf;Update max performance if current is better
return maxPerf % MOD;Return the maximum performance modulo 10^9+7 as required
⏱
Complexity
TimeO(n choose k * k) which is exponential and infeasible for large n
SpaceO(k) for recursion stack and temporary storage

Enumerating all subsets of size up to k leads to combinatorial explosion, making this approach impractical beyond small inputs.

💡 For n=20 and k=10, this means billions of combinations, which is impossible to compute in reasonable time.
Interview Verdict: TLE

This approach is too slow for large inputs but is useful to understand the problem's brute force baseline.

🧠
Sorting by Efficiency + Min-Heap to Track Speeds
💡 This approach introduces sorting by efficiency and using a min-heap to maintain the top speeds, which drastically reduces complexity and is a common optimization pattern.

Intuition

Sort engineers by efficiency descending. Iterate through them, maintaining a heap of speeds of chosen engineers. For each engineer, consider them as the minimum efficiency, and keep the top k speeds to maximize sum speed.

Algorithm

  1. Pair each engineer's speed and efficiency.
  2. Sort engineers by efficiency in descending order.
  3. Initialize a min-heap to keep track of speeds and a variable sum_speed = 0.
  4. Iterate over engineers in sorted order:
  5. - Add current speed to min-heap and sum_speed.
  6. - If heap size exceeds k, remove the smallest speed and adjust sum_speed.
  7. - Calculate performance = sum_speed * current efficiency and update max performance.
💡 The key insight is that by iterating from highest to lowest efficiency, the current engineer's efficiency is the minimum efficiency for the team.
</>
Code
import heapq

MOD = 10**9 + 7

def maxPerformance(n, speed, efficiency, k):
    engineers = sorted(zip(efficiency, speed), key=lambda x: -x[0])
    max_perf = 0
    speed_heap = []
    sum_speed = 0
    for eff, spd in engineers:
        heapq.heappush(speed_heap, spd)
        sum_speed += spd
        if len(speed_heap) > k:
            removed = heapq.heappop(speed_heap)
            sum_speed -= removed
        max_perf = max(max_perf, sum_speed * eff)
    return max_perf % MOD

# Example usage
if __name__ == '__main__':
    n = 6
    speed = [2,10,3,1,5,8]
    efficiency = [5,4,3,9,7,2]
    k = 2
    print(maxPerformance(n, speed, efficiency, k))
Line Notes
engineers = sorted(zip(efficiency, speed), key=lambda x: -x[0])Sort engineers by efficiency descending to fix minimum efficiency progressively
speed_heap = []Min-heap to keep track of speeds in current team
heapq.heappush(speed_heap, spd)Add current speed to heap to consider for team
if len(speed_heap) > k:Ensure team size does not exceed k by removing smallest speed
max_perf = max(max_perf, sum_speed * eff)Calculate performance with current minimum efficiency and update max
import java.util.*;

public class Solution {
    static final int MOD = 1000000007;

    public static int maxPerformance(int n, int[] speed, int[] efficiency, int k) {
        int[][] engineers = new int[n][2];
        for (int i = 0; i < n; i++) {
            engineers[i][0] = efficiency[i];
            engineers[i][1] = speed[i];
        }
        Arrays.sort(engineers, (a, b) -> b[0] - a[0]);
        PriorityQueue<Integer> speedHeap = new PriorityQueue<>();
        long sumSpeed = 0, maxPerf = 0;
        for (int[] eng : engineers) {
            speedHeap.offer(eng[1]);
            sumSpeed += eng[1];
            if (speedHeap.size() > k) {
                sumSpeed -= speedHeap.poll();
            }
            maxPerf = Math.max(maxPerf, sumSpeed * eng[0]);
        }
        return (int)(maxPerf % MOD);
    }

    public static void main(String[] args) {
        int n = 6;
        int[] speed = {2,10,3,1,5,8};
        int[] efficiency = {5,4,3,9,7,2};
        int k = 2;
        System.out.println(maxPerformance(n, speed, efficiency, k));
    }
}
Line Notes
Arrays.sort(engineers, (a, b) -> b[0] - a[0]);Sort engineers by efficiency descending
PriorityQueue<Integer> speedHeap = new PriorityQueue<>();Min-heap to track speeds in current team
speedHeap.offer(eng[1]);Add current speed to heap
if (speedHeap.size() > k)Remove smallest speed if team size exceeds k
maxPerf = Math.max(maxPerf, sumSpeed * eng[0]);Update max performance with current minimum efficiency
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

const int MOD = 1e9 + 7;

int maxPerformance(int n, vector<int>& speed, vector<int>& efficiency, int k) {
    vector<pair<int,int>> engineers;
    for (int i = 0; i < n; i++) {
        engineers.push_back({efficiency[i], speed[i]});
    }
    sort(engineers.rbegin(), engineers.rend());
    priority_queue<int, vector<int>, greater<int>> speedHeap;
    long long sumSpeed = 0, maxPerf = 0;
    for (auto& eng : engineers) {
        speedHeap.push(eng.second);
        sumSpeed += eng.second;
        if ((int)speedHeap.size() > k) {
            sumSpeed -= speedHeap.top();
            speedHeap.pop();
        }
        maxPerf = max(maxPerf, sumSpeed * eng.first);
    }
    return maxPerf % MOD;
}

int main() {
    int n = 6;
    vector<int> speed = {2,10,3,1,5,8};
    vector<int> efficiency = {5,4,3,9,7,2};
    int k = 2;
    cout << maxPerformance(n, speed, efficiency, k) << endl;
    return 0;
}
Line Notes
sort(engineers.rbegin(), engineers.rend());Sort engineers by efficiency descending
priority_queue<int, vector<int>, greater<int>> speedHeap;Min-heap to track speeds in current team
speedHeap.push(eng.second);Add current speed to heap
if ((int)speedHeap.size() > k)Remove smallest speed if team size exceeds k
maxPerf = max(maxPerf, sumSpeed * eng.first);Update max performance with current minimum efficiency
class MinHeap {
    constructor() {
        this.heap = [];
    }
    size() {
        return this.heap.length;
    }
    push(val) {
        this.heap.push(val);
        this.bubbleUp();
    }
    pop() {
        if (this.size() === 1) return this.heap.pop();
        const top = this.heap[0];
        this.heap[0] = this.heap.pop();
        this.bubbleDown();
        return top;
    }
    bubbleUp() {
        let idx = this.heap.length - 1;
        while (idx > 0) {
            let parent = Math.floor((idx - 1) / 2);
            if (this.heap[parent] <= this.heap[idx]) break;
            [this.heap[parent], this.heap[idx]] = [this.heap[idx], this.heap[parent]];
            idx = parent;
        }
    }
    bubbleDown() {
        let idx = 0;
        const length = this.heap.length;
        while (true) {
            let left = 2 * idx + 1;
            let right = 2 * idx + 2;
            let smallest = idx;
            if (left < length && this.heap[left] < this.heap[smallest]) smallest = left;
            if (right < length && this.heap[right] < this.heap[smallest]) smallest = right;
            if (smallest === idx) break;
            [this.heap[smallest], this.heap[idx]] = [this.heap[idx], this.heap[smallest]];
            idx = smallest;
        }
    }
}

const MOD = 1e9 + 7;

function maxPerformance(n, speed, efficiency, k) {
    const engineers = efficiency.map((eff, i) => [eff, speed[i]]);
    engineers.sort((a, b) => b[0] - a[0]);
    const speedHeap = new MinHeap();
    let sumSpeed = 0, maxPerf = 0;
    for (const [eff, spd] of engineers) {
        speedHeap.push(spd);
        sumSpeed += spd;
        if (speedHeap.size() > k) {
            sumSpeed -= speedHeap.pop();
        }
        maxPerf = Math.max(maxPerf, sumSpeed * eff);
    }
    return maxPerf % MOD;
}

// Example usage
console.log(maxPerformance(6, [2,10,3,1,5,8], [5,4,3,9,7,2], 2));
Line Notes
engineers.sort((a, b) => b[0] - a[0]);Sort engineers by efficiency descending
const speedHeap = new MinHeap();Min-heap to track speeds in current team
speedHeap.push(spd);Add current speed to heap
if (speedHeap.size() > k)Remove smallest speed if team size exceeds k
maxPerf = Math.max(maxPerf, sumSpeed * eff);Update max performance with current minimum efficiency
⏱
Complexity
TimeO(n log n) due to sorting and heap operations
SpaceO(k) for the heap storing speeds

Sorting takes O(n log n), and each engineer is pushed and popped at most once from the heap, each operation O(log k).

💡 For n=10^5 and k=10^5, this approach is efficient enough to run within time limits.
Interview Verdict: Accepted

This approach is optimal and commonly expected in interviews for this problem.

🧠
Optimized Sorting + Min-Heap with Early Pruning
💡 This approach refines the previous by emphasizing early pruning and careful heap management to handle large inputs efficiently.

Intuition

By sorting engineers by efficiency descending and maintaining a min-heap of speeds, we ensure the minimum efficiency is fixed at each iteration. We prune the smallest speeds when exceeding k to maximize sum speed, thus maximizing performance.

Algorithm

  1. Pair and sort engineers by efficiency descending.
  2. Initialize a min-heap and sum_speed = 0.
  3. Iterate through sorted engineers:
  4. - Add current speed to heap and sum_speed.
  5. - If heap size exceeds k, remove smallest speed and update sum_speed.
  6. - Calculate performance with current efficiency and update max performance.
  7. Return max performance modulo 10^9+7.
💡 The algorithm efficiently maintains the best team composition for each minimum efficiency by using a heap to track speeds.
</>
Code
import heapq

MOD = 10**9 + 7

def maxPerformance(n, speed, efficiency, k):
    engineers = sorted(zip(efficiency, speed), key=lambda x: -x[0])
    max_perf = 0
    speed_heap = []
    sum_speed = 0
    for eff, spd in engineers:
        heapq.heappush(speed_heap, spd)
        sum_speed += spd
        if len(speed_heap) > k:
            sum_speed -= heapq.heappop(speed_heap)
        max_perf = max(max_perf, sum_speed * eff)
    return max_perf % MOD

if __name__ == '__main__':
    n = 6
    speed = [2,10,3,1,5,8]
    efficiency = [5,4,3,9,7,2]
    k = 2
    print(maxPerformance(n, speed, efficiency, k))
Line Notes
engineers = sorted(zip(efficiency, speed), key=lambda x: -x[0])Sort engineers by efficiency descending to fix minimum efficiency
heapq.heappush(speed_heap, spd)Add current speed to min-heap to maintain team speeds
if len(speed_heap) > k:Remove smallest speed to keep team size within k
max_perf = max(max_perf, sum_speed * eff)Update max performance with current minimum efficiency
import java.util.*;

public class Solution {
    static final int MOD = 1000000007;

    public static int maxPerformance(int n, int[] speed, int[] efficiency, int k) {
        int[][] engineers = new int[n][2];
        for (int i = 0; i < n; i++) {
            engineers[i][0] = efficiency[i];
            engineers[i][1] = speed[i];
        }
        Arrays.sort(engineers, (a, b) -> b[0] - a[0]);
        PriorityQueue<Integer> speedHeap = new PriorityQueue<>();
        long sumSpeed = 0, maxPerf = 0;
        for (int[] eng : engineers) {
            speedHeap.offer(eng[1]);
            sumSpeed += eng[1];
            if (speedHeap.size() > k) {
                sumSpeed -= speedHeap.poll();
            }
            maxPerf = Math.max(maxPerf, sumSpeed * eng[0]);
        }
        return (int)(maxPerf % MOD);
    }

    public static void main(String[] args) {
        int n = 6;
        int[] speed = {2,10,3,1,5,8};
        int[] efficiency = {5,4,3,9,7,2};
        int k = 2;
        System.out.println(maxPerformance(n, speed, efficiency, k));
    }
}
Line Notes
Arrays.sort(engineers, (a, b) -> b[0] - a[0]);Sort engineers by efficiency descending
speedHeap.offer(eng[1]);Add current speed to min-heap
if (speedHeap.size() > k)Remove smallest speed if team size exceeds k
maxPerf = Math.max(maxPerf, sumSpeed * eng[0]);Update max performance with current minimum efficiency
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

const int MOD = 1e9 + 7;

int maxPerformance(int n, vector<int>& speed, vector<int>& efficiency, int k) {
    vector<pair<int,int>> engineers;
    for (int i = 0; i < n; i++) {
        engineers.push_back({efficiency[i], speed[i]});
    }
    sort(engineers.rbegin(), engineers.rend());
    priority_queue<int, vector<int>, greater<int>> speedHeap;
    long long sumSpeed = 0, maxPerf = 0;
    for (auto& eng : engineers) {
        speedHeap.push(eng.second);
        sumSpeed += eng.second;
        if ((int)speedHeap.size() > k) {
            sumSpeed -= speedHeap.top();
            speedHeap.pop();
        }
        maxPerf = max(maxPerf, sumSpeed * eng.first);
    }
    return maxPerf % MOD;
}

int main() {
    int n = 6;
    vector<int> speed = {2,10,3,1,5,8};
    vector<int> efficiency = {5,4,3,9,7,2};
    int k = 2;
    cout << maxPerformance(n, speed, efficiency, k) << endl;
    return 0;
}
Line Notes
sort(engineers.rbegin(), engineers.rend());Sort engineers by efficiency descending
speedHeap.push(eng.second);Add current speed to min-heap
if ((int)speedHeap.size() > k)Remove smallest speed if team size exceeds k
maxPerf = max(maxPerf, sumSpeed * eng.first);Update max performance with current minimum efficiency
class MinHeap {
    constructor() {
        this.heap = [];
    }
    size() {
        return this.heap.length;
    }
    push(val) {
        this.heap.push(val);
        this.bubbleUp();
    }
    pop() {
        if (this.size() === 1) return this.heap.pop();
        const top = this.heap[0];
        this.heap[0] = this.heap.pop();
        this.bubbleDown();
        return top;
    }
    bubbleUp() {
        let idx = this.heap.length - 1;
        while (idx > 0) {
            let parent = Math.floor((idx - 1) / 2);
            if (this.heap[parent] <= this.heap[idx]) break;
            [this.heap[parent], this.heap[idx]] = [this.heap[idx], this.heap[parent]];
            idx = parent;
        }
    }
    bubbleDown() {
        let idx = 0;
        const length = this.heap.length;
        while (true) {
            let left = 2 * idx + 1;
            let right = 2 * idx + 2;
            let smallest = idx;
            if (left < length && this.heap[left] < this.heap[smallest]) smallest = left;
            if (right < length && this.heap[right] < this.heap[smallest]) smallest = right;
            if (smallest === idx) break;
            [this.heap[smallest], this.heap[idx]] = [this.heap[idx], this.heap[smallest]];
            idx = smallest;
        }
    }
}

const MOD = 1e9 + 7;

function maxPerformance(n, speed, efficiency, k) {
    const engineers = efficiency.map((eff, i) => [eff, speed[i]]);
    engineers.sort((a, b) => b[0] - a[0]);
    const speedHeap = new MinHeap();
    let sumSpeed = 0, maxPerf = 0;
    for (const [eff, spd] of engineers) {
        speedHeap.push(spd);
        sumSpeed += spd;
        if (speedHeap.size() > k) {
            sumSpeed -= speedHeap.pop();
        }
        maxPerf = Math.max(maxPerf, sumSpeed * eff);
    }
    return maxPerf % MOD;
}

console.log(maxPerformance(6, [2,10,3,1,5,8], [5,4,3,9,7,2], 2));
Line Notes
engineers.sort((a, b) => b[0] - a[0]);Sort engineers by efficiency descending
speedHeap.push(spd);Add current speed to min-heap
if (speedHeap.size() > k)Remove smallest speed if team size exceeds k
maxPerf = Math.max(maxPerf, sumSpeed * eff);Update max performance with current minimum efficiency
⏱
Complexity
TimeO(n log n) due to sorting and heap operations
SpaceO(k) for the heap

Sorting and heap operations dominate runtime, suitable for large inputs.

💡 This approach is the practical and interview-ready solution for large constraints.
Interview Verdict: Accepted

This is the optimal and recommended approach to implement in interviews.

📊
All Approaches - One-Glance Tradeoffs
💡 In interviews, always aim to code the sorting + min-heap approach as it balances clarity and efficiency.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute ForceO(n choose k * k) exponentialO(k)Yes (deep recursion in combinations)N/AMention only - never code
2. Sorting + Min-HeapO(n log n)O(k)NoYes, team members can be trackedCode this approach
3. Optimized Sorting + Min-HeapO(n log n)O(k)NoYesCode this approach (same as #2, refined)
💼
Interview Strategy
💡 Use this guide to understand the problem deeply, practice coding the optimal approach, and prepare to explain tradeoffs and edge cases clearly.

How to Present

Step 1: Clarify problem constraints and definitions.Step 2: Describe brute force approach and its limitations.Step 3: Introduce sorting by efficiency and min-heap optimization.Step 4: Code the optimal solution carefully.Step 5: Test with examples and discuss edge cases.

Time Allocation

Clarify: 5min → Approach: 10min → Code: 15min → Test: 5min. Total ~35min

What the Interviewer Tests

Ability to optimize brute force using sorting and heaps, understanding of greedy and heap data structures, and handling large inputs efficiently.

Common Follow-ups

  • What if k equals n? → The algorithm still works efficiently.
  • Can you explain why sorting by efficiency is key? → It fixes the minimum efficiency, simplifying the problem.
💡 These follow-ups test understanding of the core insight and scalability of the solution.
🔍
Pattern Recognition

When to Use

1) Need to optimize team or subset performance; 2) Two attributes per element with one as bottleneck; 3) Constraints on subset size; 4) Maximizing product of sum and min attribute.

Signature Phrases

maximum performance of a teamminimum efficiency among team membersteam size at most k

NOT This Pattern When

Problems that only require sorting or simple greedy without a heap or minimum attribute constraint

Similar Problems

Maximum Sum of K Non-Overlapping Subarrays - similar use of sliding window and heapsTask Scheduler - scheduling with constraints and prioritiesFind K Pairs with Smallest Sums - uses heaps to maintain top k elements

Practice

(1/5)
1. Given the following code snippet for finding employee free time, what is the final output when the input schedule is [[[1,2],[5,6]], [[1,3]], [[4,10]]]?
from typing import List

def employeeFreeTime(schedule: List[List[List[int]]]) -> List[List[int]]:
    intervals = [iv for employee in schedule for iv in employee]
    intervals.sort(key=lambda x: x[0])
    free_times = []
    prev_end = intervals[0][1]
    for i in range(1, len(intervals)):
        start, end = intervals[i]
        if start > prev_end:
            free_times.append([prev_end, start])
        prev_end = max(prev_end, end)
    return free_times

schedule = [[[1,2],[5,6]], [[1,3]], [[4,10]]]
print(employeeFreeTime(schedule))
easy
A. [[3,4]]
B. [[3,4], [6,10]]
C. [[2,4]]
D. [[3,4]]

Solution

  1. Step 1: Sort all intervals

    Flattened intervals: [1,2], [5,6], [1,3], [4,10]. Sorted by start: [1,2], [1,3], [4,10], [5,6].
  2. Step 2: Merge intervals and find gaps

    Initialize prev_end=2. Iterate: - Interval [1,3]: start=1 ≤ prev_end=2, update prev_end=max(2,3)=3 - Interval [4,10]: start=4 > prev_end=3, gap [3,4] added, update prev_end=10 - Interval [5,6]: start=5 ≤ prev_end=10, update prev_end=max(10,6)=10 Return free_times=[[3,4]].
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Only one gap between merged intervals is [3,4] [OK]
Hint: Sort intervals and track max end to find gaps [OK]
Common Mistakes:
  • Off-by-one errors in gap detection
  • Including zero-length intervals
  • Incorrect sorting leading to wrong gaps
2. You need to continuously receive numbers from a stream and efficiently find the median after each insertion. Which approach guarantees an optimal solution with O(log n) insertion and O(1) median retrieval?
easy
A. Maintain two heaps: a max-heap for the lower half and a min-heap for the upper half, balancing their sizes.
B. Sort the entire list after each insertion and pick the middle element(s).
C. Use a balanced binary search tree to insert and find median in O(log n) time.
D. Use a single min-heap to store all elements and extract median by popping half the elements.

Solution

  1. Step 1: Understand the problem constraints

    The problem requires efficient insertion and median retrieval from a data stream.
  2. Step 2: Evaluate approaches

    Sorting after each insertion is O(n log n), too slow. A balanced BST can do insertions in O(log n) but median retrieval is not O(1). Using a single min-heap cannot separate lower and upper halves, so median calculation is incorrect. Maintaining two heaps (max-heap for lower half, min-heap for upper half) allows O(log n) insertion and O(1) median retrieval by balancing sizes.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Two heaps balance halves for median -> [OK]
Hint: Two heaps balance halves for median retrieval [OK]
Common Mistakes:
  • Sorting entire list each time is too slow
  • Using one heap cannot separate halves
  • Assuming balanced BST gives O(1) median retrieval
3. You are given a list of intervals, each with a start and end time. For each interval, you want to find the interval with the smallest start time that is greater than or equal to the end time of the current interval. Which approach guarantees an optimal solution in terms of time complexity?
easy
A. Use two max-heaps: one for interval starts and one for interval ends, to efficiently find the next interval.
B. Use a greedy algorithm that picks the earliest finishing interval first.
C. Sort intervals by start time and use dynamic programming to find the next interval.
D. Use a brute force nested loop to compare each interval with every other interval.

Solution

  1. Step 1: Understand problem requirements

    The problem requires finding, for each interval, the next interval with the smallest start time ≥ current interval's end time.
  2. Step 2: Evaluate approaches

    Brute force (nested loops) is correct but inefficient (O(n²)). Sorting + binary search is efficient but requires extra steps. The two max-heaps approach efficiently tracks max starts and ends to find next intervals in O(n log n) time, guaranteeing optimality.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Two heaps approach matches the problem's need for efficient next interval lookup [OK]
Hint: Two heaps efficiently track max starts and ends [OK]
Common Mistakes:
  • Assuming greedy earliest finish works
  • Using DP unnecessarily
  • Brute force accepted as optimal
4. If the data stream allows removal of arbitrary elements (not just the median), which modification to the balanced two heaps approach is necessary to maintain efficient median retrieval?
hard
A. Implement lazy removal with a delayed dictionary to mark elements for removal and prune heaps lazily.
B. Use a balanced binary search tree instead of heaps for O(log n) insertions and removals.
C. Keep a sorted list and perform binary search for insertions and removals.
D. Rebuild both heaps from scratch after each removal to maintain order.

Solution

  1. Step 1: Understand removal challenges

    Removing arbitrary elements from heaps is not efficient since heaps do not support O(log n) arbitrary removals.
  2. Step 2: Evaluate solutions

    Rebuilding heaps each removal is O(n log n), too slow. Balanced BST supports removals but is more complex and median retrieval is not O(1). Sorted list has O(n) insertion/removal. Lazy removal uses a delayed dictionary to mark elements for removal and prunes them lazily during heap operations, maintaining O(log n) amortized complexity.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Lazy removal enables efficient arbitrary removals -> [OK]
Hint: Lazy removal with delayed map enables efficient arbitrary removals [OK]
Common Mistakes:
  • Rebuilding heaps after each removal is inefficient
  • Assuming heaps support O(log n) arbitrary removals directly
  • Switching to balanced BST loses O(1) median retrieval
5. Suppose projects can be selected multiple times (reused) in the IPO problem. Which modification to the two-heaps algorithm correctly handles this scenario?
hard
A. Use a max-heap only and pick the top K profitable projects ignoring capital constraints
B. Do not remove projects from min-capital heap; instead, keep all projects always available
C. After selecting a project, reinsert it into the min-capital heap to allow reuse
D. After selecting a project, do not remove it from max-profit heap to allow repeated selection

Solution

  1. Step 1: Understand reuse implication

    Allowing reuse means projects remain available after selection, so they must be reinserted to be considered again.
  2. Step 2: Modify algorithm accordingly

    After popping a project from min-capital heap and selecting it, reinsert it to allow future reuse; otherwise, it disappears from consideration.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Reinserting projects enables multiple selections [OK]
Hint: Reinsert projects after selection to allow reuse [OK]
Common Mistakes:
  • Ignoring reinsertion, losing projects after first pick
  • Keeping all projects always available breaks capital filtering
  • Ignoring capital constraints when reusing projects
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard