A clear explanation of finding the minimum ship capacity to deliver all packages within D days using binary search.
Problem Restatement
We have packages with weights given in an array weights.
A ship loads packages in order every day. Once a day’s load would exceed the ship’s capacity, we stop loading and start a new day.
We are given days, the maximum number of days allowed.
We need to return the minimum weight capacity of the ship so that all packages can be shipped within days days.
The official constraints state that 1 <= days <= weights.length <= 5 * 10^4 and 1 <= weights[i] <= 500.
Input and Output
| Item | Meaning |
|---|---|
| Input | Array weights and integer days |
| Output | Minimum ship capacity |
| Constraint | Packages must be shipped in order |
Function shape:
def shipWithinDays(weights: list[int], days: int) -> int:
...Examples
Example 1:
weights = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
days = 5With capacity 15: day 1 gets [1,2,3,4,5], day 2 gets [6,7], day 3 gets [8], day 4 gets [9], day 5 gets [10]. Total 5 days. ✓
Answer:
15Example 2:
weights = [3, 2, 2, 4, 1, 4]
days = 3With capacity 6: day 1 [3,2], day 2 [2,4], day 3 [1,4]. All fit in 3 days.
Answer:
6Key Insight
The minimum possible capacity is max(weights) (must fit the heaviest package).
The maximum useful capacity is sum(weights) (ship everything in one day).
The answer is monotone: if capacity c works, any c' > c also works.
This makes binary search on the capacity applicable.
Algorithm
- Set
left = max(weights)andright = sum(weights). - While
left < right:mid = (left + right) // 2.- Check if capacity
midallows shipping withindaysdays. - If yes, try smaller:
right = mid. - If no, try larger:
left = mid + 1.
- Return
left.
The feasibility check: greedily load packages day by day, counting how many days are needed.
Correctness
Binary search finds the smallest mid for which the feasibility check passes.
The feasibility check is greedy: load as many packages as possible each day without exceeding capacity. This uses the fewest days possible for a given capacity.
If the greedy check says days days suffice, the capacity works. If not, we need more capacity.
Edge Cases
- Check the smallest and largest valid search values; off-by-one errors usually appear at the boundaries.
- Decide whether the predicate is looking for the first true value, the last true value, or an exact match.
- When the target is absent, the loop should still terminate and return the problem-specific failure value.
Complexity
Let n = len(weights) and S = sum(weights).
| Metric | Value | Why |
|---|---|---|
| Time | O(n log S) | Binary search over capacity range, O(n) check each time |
| Space | O(1) | Constant extra space |
Common Pitfalls
- Do not mix inclusive and half-open bounds inside the same loop.
- Make sure each branch strictly reduces the search interval.
Implementation
class Solution:
def shipWithinDays(self, weights: list[int], days: int) -> int:
def can_ship(capacity: int) -> bool:
current = 0
needed_days = 1
for w in weights:
if current + w > capacity:
needed_days += 1
current = 0
current += w
return needed_days <= days
left = max(weights)
right = sum(weights)
while left < right:
mid = (left + right) // 2
if can_ship(mid):
right = mid
else:
left = mid + 1
return leftCode Explanation
The feasibility function can_ship simulates the loading process:
for w in weights:
if current + w > capacity:
needed_days += 1
current = 0
current += wStart a new day when the current load would exceed capacity.
Binary search narrows the capacity range:
while left < right:
mid = (left + right) // 2
if can_ship(mid):
right = mid
else:
left = mid + 1When left == right, we have the minimum capacity.
Testing
def run_tests():
s = Solution()
assert s.shipWithinDays([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5) == 15
assert s.shipWithinDays([3, 2, 2, 4, 1, 4], 3) == 6
assert s.shipWithinDays([1, 2, 3, 1, 1], 4) == 3
assert s.shipWithinDays([1], 1) == 1
print("all tests passed")
run_tests()| Test | Expected | Why |
|---|---|---|
| 10 packages, 5 days | 15 | Optimal grouping fits in 5 days |
| 6 packages, 3 days | 6 | Each day gets balanced load |
| 5 packages, 4 days | 3 | Small capacity works |
| 1 package, 1 day | 1 | Minimum case |