Skip to content

Commit 43b99df

Browse files
committed
refactor(datastructures, algorithms, heaps): add variation and doc to min cost to hire k workers
1 parent 8c8622f commit 43b99df

File tree

4 files changed

+101
-16
lines changed

4 files changed

+101
-16
lines changed

algorithms/heap/min_cost_hire_k_workers/README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,51 @@ Explanation: We pay 4 to 0th worker, 13.33333 to 2nd and 3rd workers separately.
3030
- Greedy
3131
- Sorting
3232
- Heap(Priority Queue)
33+
34+
## Solution
35+
36+
This solution uses the top k elements pattern, commonly used when selecting the top k items based on a certain criterion.
37+
Here, we need to form a group of exactly k workers and minimize the total cost while ensuring fair payments based on the
38+
workers’ wage expectations and quality. Below are some key observations that will help us to solve the problem:
39+
40+
1. **Proportionality condition**: All workers in the group must be paid proportionally to their quality. If one worker’s
41+
quality is twice that of another, they must be paid twice as much.
42+
2. **Wage-to-quality ratio**: For each worker, we calculate a ratio that indicates how much we need to pay per unit of
43+
quality. This ratio is defined as: `ratio[i] = wage[i]/quality[i]`
44+
3. **Greedy approach:** To minimize the total wage, we process workers based on their wage-to-quality ratios in ascending
45+
order. Once we select k workers, we fix the highest wage-to-quality ratio among them. All workers are paid according
46+
to this ratio, ensuring proportionality.
47+
4. **Optimization using a heap**: We can use a max heap to efficiently manage the total quality of selected workers. This
48+
allows us to keep track of the workers with the smallest total quality, which is important for minimizing the total
49+
wage. The max heap allows us to quickly access the largest quality (at the top of the heap) and remove it in constant
50+
time, ensuring that only k workers remain.
51+
52+
The steps of the algorithm are as below:
53+
54+
1. Creates a list of tuple workers for each worker. Sort these tuples based on the ratios in ascending order to process
55+
workers with the lowest ratio first. Each tuple contains:
56+
- The worker’s wage-to-quality ratio (w/q)
57+
- The worker’s quality (q)
58+
2. Create a max heap `heap` to store the qualities of the selected workers.
59+
3. Initialize the below variables:
60+
- A variable total_quality is used to track the total quality of the workers in the heap
61+
- A variable min_cost to store the minimum cost encountered during the process
62+
4. Iterate through the sorted list of workers based on their ratios. For each worker:
63+
- Add their quality (as a negative value to simulate max heap behavior) to the heap.
64+
- Update the total_quality by adding the current worker’s quality.
65+
5. After adding a worker, check if the heap contains more than k workers. If so, remove the worker with the highest
66+
quality (i.e., the smallest negative value) to minimize the total quality used in cost calculations.
67+
6. Once the heap contains exactly k workers, compute the cost of hiring them. This is done by multiplying the highest
68+
wage-to-quality ratio (of the current worker) by the total_quality of the selected group. Update the min_cost if the
69+
current one is lower than the previously recorded minimum.
70+
7. After processing all workers, return the min_cost calculated.
71+
72+
### Time Complexity
73+
74+
Sorting the workers takes `O(nlog(n))` time, and maintaining the heap takes `O(klog(k))` time where n is the total number
75+
of elements in workers and k is the number of workers we want to hire. Hence, the overall time complexity is `O(nlog(n))`
76+
77+
### Space Complexity
78+
79+
We use additional space for the list of all workers (`O(n)`) and for the heap, which contains at most k workers (`O(k)`).
80+
Since `k≤n`, the overall space complexity is `O(n)`.

algorithms/heap/min_cost_hire_k_workers/__init__.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,31 @@ def min_cost_to_hire_workers(quality: List[int], wage: List[int], k: int) -> flo
2222
quality_sum -= -heappop(worker_pool)
2323

2424
return float(result)
25+
26+
27+
def min_cost_to_hire_workers_2(quality, wage, k):
28+
# Step 1: Create a list of tuples with (wage/quality ratio, quality) for each worker
29+
workers = sorted([(w / q, q) for w, q in zip(wage, quality)])
30+
31+
heap = [] # A max-heap (using negative values for qualities)
32+
total_quality = 0 # Sum of the qualities of the selected workers
33+
min_cost = float('inf') # Initialize the minimum cost to infinity
34+
35+
# Step 2: Iterate through each worker sorted by their wage-to-quality ratio
36+
for ratio, q in workers:
37+
# Add the worker's quality to the heap (negative to simulate a max-heap)
38+
heappush(heap, -q)
39+
total_quality += q # Add the worker's quality to the total quality
40+
41+
# Step 3: If we have more than k workers, remove the one with the largest quality
42+
if len(heap) > k:
43+
# Remove the largest quality (smallest negative number)
44+
total_quality += heappop(heap) # Adding the negative value back
45+
46+
# Step 4: If we have exactly k workers, calculate the cost
47+
if len(heap) == k:
48+
# The cost is the current ratio multiplied by the total quality of k workers
49+
min_cost = min(min_cost, ratio * total_quality)
50+
51+
# Step 5: Return the minimum cost found
52+
return min_cost

algorithms/heap/min_cost_hire_k_workers/test_min_cost_to_hire_workers.py

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
11
import unittest
2-
from . import min_cost_to_hire_workers
2+
from typing import List
3+
from parameterized import parameterized
4+
from algorithms.heap.min_cost_hire_k_workers import min_cost_to_hire_workers, min_cost_to_hire_workers_2
5+
6+
MIN_COST_TO_HIRE_K_WORKERS_TEST_CASES = [
7+
([10, 20, 5], [70, 50, 30], 2, 105.00000),
8+
([3, 1, 10, 10, 1], [4, 8, 2, 2, 7], 3, 30.66667),
9+
([10, 10, 10], [50, 60, 70], 2, 120.00000),
10+
([2, 3, 1], [5, 6, 2], 2, 7.50000),
11+
([5, 9, 4], [50, 45, 30], 3, 180.00000),
12+
([7, 8, 6, 10], [70, 90, 80, 100], 2, 168.75000),
13+
]
314

415

516
class MinCostToHireWorkersTestCase(unittest.TestCase):
6-
def test_1(self):
7-
"""quality = [10,20,5], wage = [70,50,30], k = 2 returns 105.00000"""
8-
quality = [10, 20, 5]
9-
wage = [70, 50, 30]
10-
k = 2
11-
expected = 105.00000
17+
@parameterized.expand(MIN_COST_TO_HIRE_K_WORKERS_TEST_CASES)
18+
def test_min_cost_to_hire_k_workers(
19+
self, quality: List[int], wage: List[int], k: int, expected: float
20+
):
1221
actual = min_cost_to_hire_workers(quality, wage, k)
13-
self.assertEqual(expected, actual)
22+
self.assertEqual(expected, round(actual, 5))
1423

15-
def test_2(self):
16-
"""quality = [3,1,10,10,1], wage = [4,8,2,2,7], k = 3 returns 30.66667"""
17-
quality = [3, 1, 10, 10, 1]
18-
wage = [4, 8, 2, 2, 7]
19-
k = 3
20-
expected = 30.66667
21-
actual = round(min_cost_to_hire_workers(quality, wage, k), 5)
22-
self.assertEqual(expected, actual)
24+
@parameterized.expand(MIN_COST_TO_HIRE_K_WORKERS_TEST_CASES)
25+
def test_min_cost_to_hire_k_workers_2(
26+
self, quality: List[int], wage: List[int], k: int, expected: float
27+
):
28+
actual = min_cost_to_hire_workers_2(quality, wage, k)
29+
self.assertEqual(expected, round(actual, 5))
2330

2431

2532
if __name__ == "__main__":

datastructures/stacks/minstack/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# Min Stack
2+
13
Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
24

35
Implement the MinStack class:

0 commit comments

Comments
 (0)