Problem overview
The function minTime receives an IntArray called times and an Int called K. Worker i needs times[i] units of time per product, and all workers pack at the same time. The function returns the smallest time in which they pack at least K products together.
For times = [4, 7, 3, 6, 7, 1] and K = 60, the answer is 30. In 30 units the workers pack 7 + 4 + 10 + 5 + 4 + 30 = 60 products, and in 29 units only 57.
In time units a worker with time t per product packs time / t products, rounded down.
Approach: binary search on the answer
The brute-force way tries time 0, 1, 2 and so on until the workers reach K products. That works, but the answer can be huge. The statement's budget here is O(n * log(k * T)), which suggests a binary search over the possible times instead.
The search is possible because the check is monotonic: if the workers can pack K products in some time, they can do it in any longer time too. Below the answer, every time gives fewer than K products. From the answer on, every time gives at least K. A binary search finds the boundary between the two groups.
It needs a range to search. The lower end is 1. The upper end comes from the fastest worker, with time minValue: working alone, that worker packs K products in minValue * K units. The whole factory can't need more, so the answer is at most minValue * K.
Each step looks at the middle time mid of the range and counts the products:
- If the count is below
K,midis too short and so is everything below it, so the search continues in the upper half. - Otherwise
midis enough. It might be the answer, so it is recorded, and the search continues in the lower half for something shorter.
With [4, 7, 3, 6, 7, 1] and K = 60, the fastest worker has minValue = 1, so the range is 1 to 60. The first middle is 30, which packs exactly 60, so 30 is recorded and the range becomes 1 to 29. The count for 15 is 3 + 2 + 5 + 2 + 2 + 15 = 29, too short, so the range moves up, and the search keeps narrowing until nothing below 30 is left. The recorded answer stays 30.
Step by step
- Find the fastest worker. Start
var minValueattimes[0]and loop withfor (t in times), updating it withminOf(minValue, t). - Set the range.
var left = 1andvar right = minValue * K, the smallest and largest times worth testing. - Keep the best answer.
var time = 0will hold the smallest time found so far that packs at leastKproducts. - Loop while the range has candidates:
left <= right. - Take the middle,
val mid = (left + right) / 2. Integer division drops the fraction. - Test it. With the helper
countProducts(times, mid), check whether the count is less thanK. If so, setleft = mid + 1. - Else record and go lower. Set
time = mid, thenright = mid - 1. Record first: the loop can end on amidthat was too short, so the last good one must already be saved. - Return
timeafter the loop. - Write the helper above
minTimeasfun countProducts(times: IntArray, time: Int): Int. It addstime / tto a runningproductsfor everytintimesand returns the total.
The solution in Kotlin
class Solution {
fun countProducts(times: IntArray, time: Int): Int {
var products = 0
for (t in times) {
products += time / t
}
return products
}
fun minTime(times: IntArray, K: Int): Int {
var minValue = times[0]
for (t in times) {
minValue = minOf(minValue, t)
}
var left = 1
var right = minValue * K
var time = 0
while (left <= right) {
val mid = (left + right) / 2
if (countProducts(times, mid) < K) {
left = mid + 1
} else {
time = mid
right = mid - 1
}
}
return time
}
}
The Kotlin details worth a closer look:
minOf.minOf(a, b)is a standard-library function that returns the smaller of two values, so no import is needed.val midinside the loop. A newvalis created on each iteration, which is fine. Onlyleft,rightandtimelive across iterations, so only they arevar.- Integer division. In
time / tand(left + right) / 2, both operands areInt, so the fraction is dropped. Intlimits.Intholds up to 2147483647 and wraps silently past that. The boundminValue * Kand the sumleft + rightmust both fit.
Complexity
Time: O(n * log(k * T)). The range holds up to minValue * K candidate times, and every iteration halves it, so there are about log2(minValue * K) iterations. Each iteration calls countProducts, which is one pass over n workers. The initial scan for minValue adds O(n).
Space: O(1). Only a few Int variables are used.
Edge cases and common mistakes
- Forgetting to record
time. Returningleftormidafter the loop is wrong when the final probe was too short. Savingmidin the else branch avoids it. K = 0. The range is 1 to 0, so the loop never runs and the function returns 0.- One worker. For
times = [5]andK = 3, the range is 1 to 15 and the answer is 15. - Discarding the wrong half. Too few products means the time is too short: go up with
left = mid + 1. Searching down there never reaches the answer. - Overflow. If
minValue * Kis larger than 2147483647, the product wraps around to a wrong bound. Fortimes = [3]andK = 1000000000, the function returns 0 instead of 3000000000. The lesson's tests stay withinInt, but a larger problem would needLong. - Starting the range at 0. It works for
Kat least 1 but wastes a step, since 0 time packs nothing.
Testing
times = [4, 7, 3, 6, 7, 1],K = 60gives 30.times = [5],K = 3gives 15.times = [2, 3],K = 5gives 6.times = [4, 7, 3, 6, 7, 1],K = 0gives 0.