Loading problems...
TL;DR: The optimal solution involves sorting workers by their wage-to-quality ratio and using a Max-Heap to maintain the sum of the lowest qualities encountered so far to minimize total cost.
The LeetCode 857 problem, "Minimum Cost to Hire K Workers," asks us to select exactly workers from a pool of candidates. Each worker has a specific quality rating and a minimum expectation. The payment rules enforce two constraints:
wageWe must determine the minimum total cost required to hire a valid group of workers.
A brute force approach would involve generating every possible combination of workers from the available candidates.
For each combination (subset) of workers:
Pseudo-code:
textmin_total_cost = infinity for each subset of size k from n workers: current_ratio = 0 current_quality_sum = 0 // Find the bottleneck ratio for worker in subset: ratio = worker.wage / worker.quality current_ratio = max(current_ratio, ratio) current_quality_sum += worker.quality total_cost = current_ratio * current_quality_sum min_total_cost = min(min_total_cost, total_cost)
Complexity Analysis: The number of ways to choose workers from is given by the binomial coefficient . In the worst case (e.g., ), this grows exponentially.
The key to solving LeetCode 857 efficiently lies in the mathematical relationship between the constraints.
Let be the ratio of pay to quality. For a worker to be satisfied, the group ratio must satisfy:
If we select a group of workers, the group ratio must be at least the maximum ratio among them to satisfy everyone. To minimize cost, we should pick the smallest valid . Therefore, the cost for a specific group is:
This equation reveals two greedy strategies:
Visual Description: Imagine sorting all workers on a line based on their unit cost (ratio). As we iterate through this line from left to right, the current worker defines the new maximum ratio allowed. At each step, we have a pool of eligible workers (all seen so far). To minimize cost, we need the workers with the lowest quality from this pool. A Max-Heap allows us to visualize this pool: the "heaviest" (highest quality) worker floats to the top. When we encounter a new worker, we add them to the pool. If the pool size exceeds , we remove the worker with the highest quality (the root of the heap) because they contribute most to the cost.

Let's trace the algorithm with quality = [10, 20, 5], wage = [70, 50, 30], k = 2.
Calculate Ratios:
Sort by Ratio:
Initialize: max_heap = [], sum_quality = 0, min_cost = Infinity.
Iteration 1 (Worker 1):
max_heap = [20]. sum_quality = 20.Iteration 2 (Worker 2):
max_heap = [20, 5]. sum_quality = 25.min_cost = 150.0.Iteration 3 (Worker 0):
max_heap = [20, 5, 10]. sum_quality = 35.max_heap = [10, 5]. sum_quality = 35 - 20 = 15.min_cost: .Result: Return 105.0.
The algorithm's correctness relies on the invariant that at any step (where we consider the -th worker in the sorted list as the highest-ratio worker), the Max-Heap contains the smallest qualities encountered in the range .
Since the list is sorted by ratio, the current worker has the maximum ratio among all workers currently in the heap. Therefore, calculating ratio[i] * sum(heap_qualities) guarantees that the payment condition is met for all workers in the heap. By consistently evicting the largest quality when the group size exceeds , we ensure that for the specific ratio ratio[i], we are multiplying it by the smallest possible sum of qualities available up to that point. This effectively checks all optimal combinations.
cpp#include <vector> #include <queue> #include <algorithm> #include <cmath> using namespace std; class Solution { public: double mincostToHireWorkers(vector<int>& quality, vector<int>& wage, int k) { int n = quality.size(); vector<pair<double, int>> workers; // Store ratio and quality. Ratio = wage / quality for (int i = 0; i < n; ++i) { double ratio = (double)wage[i] / quality[i]; workers.push_back({ratio, quality[i]}); } // Sort by ratio ascending sort(workers.begin(), workers.end()); // Max-heap to store qualities of the k workers with smallest qualities so far priority_queue<int> max_heap; double current_quality_sum = 0; double min_total_cost = 1e9 + 7; // Initialize with a large value for (const auto& worker : workers) { double ratio = worker.first; int q = worker.second; current_quality_sum += q; max_heap.push(q); // If we have more than k workers, remove the one with the highest quality if (max_heap.size() > k) { current_quality_sum -= max_heap.top(); max_heap.pop(); } // If we have exactly k workers, calculate the cost if (max_heap.size() == k) { min_total_cost = min(min_total_cost, current_quality_sum * ratio); } } return min_total_cost; } };
javaimport java.util.Arrays; import java.util.PriorityQueue; import java.util.Collections; class Solution { public double mincostToHireWorkers(int[] quality, int[] wage, int k) { int n = quality.length; double[][] workers = new double[n][2]; for (int i = 0; i < n; i++) { // workers[i][0] is ratio, workers[i][1] is quality workers[i][0] = (double) wage[i] / quality[i]; workers[i][1] = (double) quality[i]; } // Sort by ratio ascending Arrays.sort(workers, (a, b) -> Double.compare(a[0], b[0])); // Max-Heap to keep the smallest k qualities (evict largest) PriorityQueue<Double> maxHeap = new PriorityQueue<>(Collections.reverseOrder()); double currentQualitySum = 0; double minTotalCost = Double.MAX_VALUE; for (double[] worker : workers) { double ratio = worker[0]; double q = worker[1]; currentQualitySum += q; maxHeap.offer(q); if (maxHeap.size() > k) { currentQualitySum -= maxHeap.poll(); } if (maxHeap.size() == k) { minTotalCost = Math.min(minTotalCost, currentQualitySum * ratio); } } return minTotalCost; } }
pythonimport heapq class Solution: def mincostToHireWorkers(self, quality: list[int], wage: list[int], k: int) -> float: # Create a list of tuples (ratio, quality) workers = [] for q, w in zip(quality, wage): workers.append((w / q, q)) # Sort workers by their wage-to-quality ratio workers.sort(key=lambda x: x[0]) # Python's heapq is a min-heap. To simulate a max-heap, # we store negative quality values. max_heap = [] current_quality_sum = 0 min_total_cost = float('inf') for ratio, q in workers: # Add current quality to sum and push negative quality to heap current_quality_sum += q heapq.heappush(max_heap, -q) # If we exceed k workers, remove the one with the largest quality # (which is the smallest value in our negative heap) if len(max_heap) > k: largest_quality = -heapq.heappop(max_heap) current_quality_sum -= largest_quality # If we have exactly k workers, calculate cost if len(max_heap) == k: min_total_cost = min(min_total_cost, current_quality_sum * ratio) return min_total_cost
Time Complexity:
Space Complexity:
The Heap - Scheduling / Minimum Cost pattern is a powerful tool for optimization problems involving dynamic sets.
Why does my solution get Time Limit Exceeded (TLE)?
Why is the output wrong even though I sorted by quality?
Why am I using a Min-Heap instead of a Max-Heap?
Why is the cost calculation incorrect?