Python 0857.py
class Solution:
def mincostToHireWorkers(self, quality: List[int], wage: List[int], k: int) -> float:
result = inf
# sort by quality per wage
candidates = [ ( q/w, q, w ) for q, w in zip (quality, wage) ]
# the worker with lowest quality per wage determines the total cost according to rule 1
candidates.sort(reverse = True)
current = []
sum = 0
for ratio, q, w in candidates:
sum += q
heappush(current, -q) # max-heap, use negative values
# too many workers, remove those with high quality because they cost the most
# (when adjusted for rule 1)
if len(current) > k:
remove = -heappop(current)
sum -= remove
# current ratio is the lowest quality per wage
if len(current) == k:
result = min(result, sum / ratio)
return result
Difficulty
and has an acceptance rate of about 64% (200k+ submissions).
Typical patterns to solve this problem:
- Array
- Greedy
- Heap (Priority Queue)
- Sorting
AI Disclaimer
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.
See this dedicated page for more details.
Code Walkthrough
Overview
The provided implementation solves the classic “minimum cost to hire exactly k workers” problem. Each worker is described by a quality value and a minimum wage expectation. The algorithm selects a group of k workers such that the total wage paid is minimized while respecting two constraints: every worker receives at least their minimum wage, and all workers are paid a common wage-to-quality ratio. The solution proceeds by sorting workers according to their quality-to-wage ratio, then iteratively maintaining a candidate set of the k smallest qualities using a max-heap. At each step the current ratio determines the total cost for the current candidate set, and the minimum across all steps is returned.
Key Steps
The first logical block builds a list of tuples that capture each worker’s ratio, quality, and wage. The ratio is computed as quality / wage, which is the inverse of the more common wage-to-quality ratio; sorting by this value in descending order ensures that the smallest ratio encountered so far is always the current worker’s ratio.
candidates = [ ( q/w, q, w ) for q, w in zip (quality, wage) ]
candidates.sort(reverse = True)
A max-heap (current) stores the qualities of the workers that are currently being considered for the group. The variable sum tracks the total quality of the workers inside the heap. As the algorithm iterates over the sorted candidates, each worker’s quality is added to sum and the negative quality is pushed onto the heap to simulate a max-heap using Python’s heapq (which implements a min-heap).
current = []
sum = 0
for ratio, q, w in candidates:
sum += q
heappush(current, -q) # max-heap via negative values
When the heap size exceeds k, the worker with the largest quality (the most expensive when scaled by the current ratio) is removed. This removal reduces sum accordingly, guaranteeing that the heap always contains the k smallest qualities seen so far.
if len(current) > k:
remove = -heappop(current)
sum -= remove
Whenever the heap size exactly equals k, the current ratio represents the lowest quality-to-wage ratio among the selected workers. The total cost for this configuration is computed as sum / ratio, and the global minimum (result) is updated if this cost is lower.
if len(current) == k:
result = min(result, sum / ratio)
Finally, after processing all candidates, the algorithm returns the smallest total cost found.
return result
Edge Cases
The algorithm gracefully handles the situation where k equals the total number of workers. In that case the heap never exceeds size k, so no removal occurs, and the cost is computed once using the ratio of the last processed worker, which is the smallest ratio among all workers.
When k is 1, each iteration creates a heap of size one, and the cost calculation reduces to q / (q/w) = w, effectively returning the smallest wage among all workers, which satisfies the constraints.
If multiple workers share the same quality-to-wage ratio, the sorting order preserves their original relative positions, but the heap logic still ensures that only the smallest qualities are retained, so the algorithm remains correct regardless of duplicate ratios.
The use of float('inf') as the initial result guarantees that any feasible cost will replace it, even when the first valid group of k workers appears late in the iteration.
Performance Optimizations
The most significant optimization is the use of a max-heap to keep track of the k smallest qualities while iterating through the sorted list. By pushing negative qualities, the code leverages Python’s built-in min-heap implementation without needing a custom data structure. This approach ensures that each insertion and removal operation runs in logarithmic time relative to k, keeping the overall processing efficient as the algorithm scans the entire candidate list only once.
Sorting the workers by their quality-to-wage ratio up front reduces the problem to a single pass over a monotonic sequence of ratios. Because the ratio only decreases (due to the descending sort), the current ratio at any iteration is guaranteed to be the minimum ratio for the workers currently in the heap, eliminating the need for additional scans or recomputation of ratios.
The cumulative sum variable avoids recomputing the total quality of the heap on each iteration. By updating sum incrementally when workers are added or removed, the algorithm eliminates an O(k) aggregation step, further contributing to its efficiency.
Overall, the combination of a single sort, a bounded heap, and incremental aggregation yields a solution that scales well with the size of the input arrays while adhering strictly to the problem constraints.
ELI5 - Simplified Explanation
Imagine you have a big team of helpers who all want to be paid for their hard work. Each helper has a quality score that shows how well they work and a minimum wage they need. Some helpers are very fast, while others take a bit more time.
The goal is to pick a group of helpers to finish a job. There are two rules. The first rule is that if you pick a group, everyone must get paid the same amount of money for each point of quality they have. The second rule is that every single person must get at least the minimum wage they asked for.
To solve this, the code first looks at every helper and figures out their specific ratio of quality versus the money they expect. By looking at these ratios, it becomes clear how much you have to pay everyone in the group to keep them all happy.
Then, the code starts a little game of picking helpers. It sorts them so that it looks at the most efficient ones first. As it goes through the list, it adds helpers to a group one by one and keeps track of the total quality points they bring.
If the group gets too big, the code looks at all the helpers currently in the group and says goodbye to the one who has the highest quality points. This is because high quality points are expensive when you have to pay the higher group rate. By keeping only the best group of helpers for each ratio, it finds the smallest amount of money needed to pay everyone fairly. Once it checks all the possible groups, it knows the cheapest way to get the job done.
Code Quality
Complexity Analysis
As a rule of thumb, the maximum input size for one second execution time is
- factorial: up to 10 elements
- exponential: up to 20 elements
- polynomial: a few thousand elements
- loglinear: up to 100,000 elements
- linear: a few million elements
- logarithmic: a billion elements
- constant: practically unlimited
Other Solutions
Obviously, I did that after I solved the problem myself.
In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.
And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.
(click on a column header to sort the table)
| author | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| author | solutions | |||||||||
| www | AlgoMonster | C++ | Java | Python | TS | |||||
| WalkCCC | C++ | Java | Python | |||||||
| leetcode.ca | C++ | Java | Python | Go | ||||||
| LeetcodeHelp | C++ | Java | Python | |||||||
| Progiez | C++ | Java | Python | |||||||
| github | BigEggStudy | C# | ||||||||
| black-shadows | C++ | |||||||||
| charles-wangkai | Java | |||||||||
| everthis | JS | |||||||||
| fengvyi | C++ | |||||||||
| JoshCrozier | JS | |||||||||
| keineahnung2345 | C++ | |||||||||
| ruoru | Rust | |||||||||
| suxrobGM | C# | |||||||||
| trung-hn | Python | |||||||||
| varunu28 | Java | |||||||||
| vli02 | C | |||||||||
| wisdompeak | C++ | |||||||||
| AlgoMonster | C++, Python, Java, TypeScript | |||||||||
| WalkCCC | C++, Python, Java | |||||||||
| leetcode.ca | C++, Go, Java, Python | |||||||||
| LeetcodeHelp | C++, Java, Python | |||||||||
| Progiez | C++, Java, Python | |||||||||
| BigEggStudy | C# | |||||||||
| black-shadows | C++, C++ | |||||||||
| charles-wangkai | Java | |||||||||
| everthis | JavaScript | |||||||||
| fengvyi | C++ | |||||||||
| JoshCrozier | JavaScript | |||||||||
| keineahnung2345 | C++ | |||||||||
| ruoru | Rust | |||||||||
| suxrobGM | C# | |||||||||
| trung-hn | Python | |||||||||
| varunu28 | Java | |||||||||
| vli02 | C | |||||||||
| wisdompeak | C++ |
Videos
Solved On The Same Day
- problem 2231: Largest Number After Digit Swaps by Parity
- problem 2335: Minimum Amount of Time to Fill Cups
- problem 1942: The Number of the Smallest Unoccupied Chair
- problem 2285: Maximum Total Importance of Roads
Extra
- Tech Stack - my development environment and tools
- AI - how AI helped me create this website (NOT solving the problems)
- Progress - progression over time
- Quality - code quality