Capacity to Ship Packages Within D Days
Capacity to Ship Packages Within D Days
Pattern: Binary Search on Answer (predicate search) - Core
Idea:
Variations :
π» Code
Explicit answer style Binary Search :-
def shipWithinDays(weights, days):
low = max(weights)
high = sum(weights)
ans = high
def feasible(capacity):
days_used = 1
current_load = 0
for weight in weights:
if current_load + weight > capacity:
days_used += 1
current_load = 0
current_load += weight
return days_used <= days
while low <= high:
mid = low + (high - low) // 2
if feasible(mid):
ans = mid
high = mid - 1 # search for a smaller feasible capacity
else:
low = mid + 1 # need larger capacity
return ans
Time complexity - O(n log S ) , S is sum(packages) Aux. Space complexity - O(1)
LeetCode 1011 β Binary Search on Answer + Greedy Feasibility
Given an array weights, where packages must be shipped in the given order, find the minimum ship capacity needed to ship all packages within days days.
Each dayβs load cannot exceed the shipβs capacity.
Key Idea
The answer is not an index in the array. It is a number:
ship capacity β [max(weights), sum(weights)]
Instead of trying every possible capacity, binary-search the answer.
For a candidate capacity C, ask:
Can all packages be shipped within
daysdays if the shipβs capacity isC?
This is our feasibility predicate:
feasible(C)
Intuition
For a fixed capacity C, greedily load as many consecutive packages as possible into the current day.
When adding the next package would exceed C, start a new day.
Example:
weights = [1, 2, 3, 4, 5]
C = 6
Day 1: [1, 2, 3] = 6
Day 2: [4] = 4
Day 3: [5] = 5
So C = 6 requires 3 days.
Why greedy works
Packages must remain in their original order.
For a fixed capacity, taking as many consecutive packages as possible for the current day can never increase the number of days needed. Leaving usable capacity unused cannot help later because the next package must be processed in order.
Why Binary Search Works
If capacity C is feasible, then every larger capacity is also feasible:
Capacity:
1 2 3 4 5 6 7 8 9 ...
F F F F F T T T T
β
minimum feasible
So the predicate is monotonic:
Therefore we need to find the first True.
Search Space
Lower bound
low = max(weights)
A package cannot be split, so the ship must at least carry the heaviest package.
Upper bound
high = sum(weights)
With this capacity, all packages can be shipped in a single day.
Therefore:
[max(weights), sum(weights)]
is guaranteed to contain the answer.
Approach
1. Define the validator
def feasible(capacity):
days_used = 1
current_load = 0
for weight in weights:
if current_load + weight > capacity:
days_used += 1
current_load = 0
current_load += weight
return days_used <= days
2. Binary-search the first feasible capacity
There are two standard ways to implement this.
1. Implicit Answer / Boundary Style
The search interval itself represents where the answer can still be.
def shipWithinDays(weights, days):
low = max(weights)
high = sum(weights)
def feasible(capacity):
days_used = 1
current_load = 0
for weight in weights:
if current_load + weight > capacity:
days_used += 1
current_load = 0
current_load += weight
return days_used <= days
while low < high:
mid = low + (high - low) // 2
if feasible(mid):
high = mid # mid may be the answer
else:
low = mid + 1 # mid definitely cannot be answer
return low
Invariant
The answer always remains inside:
[low, high]
When low == high, only one candidate remains, so that value is the minimum feasible capacity.
2. Explicit Answer / ans Style
This uses the more traditional low <= high binary search and stores the best feasible answer separately.
def shipWithinDays(weights, days):
low = max(weights)
high = sum(weights)
ans = high
def feasible(capacity):
days_used = 1
current_load = 0
for weight in weights:
if current_load + weight > capacity:
days_used += 1
current_load = 0
current_load += weight
return days_used <= days
while low <= high:
mid = low + (high - low) // 2
if feasible(mid):
ans = mid
high = mid - 1 # search for a smaller feasible capacity
else:
low = mid + 1 # need larger capacity
return ans
Invariant
ans stores the best feasible capacity found so far.
When mid is feasible:
ans = mid
high = mid - 1
because we want to know whether an even smaller capacity also works.
Which Style Should You Prefer?
For this problem, I prefer the implicit boundary style:
while low < high:
because the problem naturally asks for:
first feasible capacity
and the final boundary directly gives the answer.
The explicit ans style is equally valid and useful to know, especially when adapting the standard low <= high binary-search template.
Donβt memorize the syntax independently. Remember the invariant:
feasible(mid)β can we move toward smaller capacities?
not feasible(mid)β capacity must increase.
Dry Run
weights = [1, 2, 3, 4, 5]
days = 3
low = 5
high = 15
Try:
mid = 10
Greedy:
Day 1: 1 + 2 + 3 + 4 = 10
Day 2: 5
2 days β€ 3 β feasible
So:
high = 10
Try:
mid = 7
Day 1: 1 + 2 + 3 = 6
Day 2: 4
Day 3: 5
3 days β feasible.
Try:
mid = 6
Day 1: 1 + 2 + 3 = 6
Day 2: 4
Day 3: 5
Still feasible.
Try:
mid = 5
Day 1: 1 + 2
Day 2: 3
Day 3: 4
Day 4: 5
4 days β not feasible.
Therefore:
5 β F
6 β T
Answer:
6
Complexity
Let:
-
= number of packages
-
=
sum(weights) -
=
max(weights)
Each feasible() call scans all packages:
Binary search performs:
checks.
Total
Usually written as:
Auxiliary Space
Only a few variables are used apart from the input.
Important Edge Cases / Quirks
1. One day
days = 1
Answer:
sum(weights)
2. Number of days equals number of packages
If every package can be shipped individually, answer is:
max(weights)
3. Package cannot be split
This is why:
low = max(weights)
is essential.
4. Order cannot be changed
You cannot rearrange packages to improve packing.
The greedy validator relies on processing them in the given order.
5. Donβt use days_used == days blindly
The correct feasibility condition is:
days_used <= days
If a capacity allows shipping in fewer days, it is still feasible.
Important Variations
Book Allocation
Allocate contiguous books to
Kstudents while minimizing the maximum pages assigned to one student.
Same structure:
candidate maximum pages
β
greedily allocate books
β
students required <= K ?
β
first feasible
Painterβs Partition
Divide contiguous boards among
Kpainters while minimizing the maximum workload.
Again:
candidate maximum workload
β
greedy partition
β
painters required <= K ?
β
first feasible
These three problems should be recognized as the same Binary Search on Answer family:
Ship Packages
Book Allocation
Painter's Partition
β
MINIMIZE THE MAXIMUM
Common Mistakes
-
β Binary-searching the
weightsarray. -
β Setting
low = 0instead ofmax(weights). -
β Using
days_used == daysinstead of<= days. -
β Reordering packages.
-
β Using
high = mid - 1in the implicit first-True formulation. -
β Forgetting that
miditself may be the answer.
Pattern Recognition
When you see:
βFind the minimum possible capacity / maximum load / maximum sum such that everything can be completed within K groups/days.β
Think immediately:
MINIMIZE THE MAXIMUM
β
Guess maximum allowed value X
β
Can I complete the task with X?
β
Greedy validator
β
FFFFTTTT
β
Binary search FIRST TRUE
Reusable template
low = maximum_required_single_item
high = total_work
while low < high:
mid = low + (high - low) // 2
if feasible(mid):
high = mid
else:
low = mid + 1
return low
Mental hook:
βCapacity is the answer. Capacity β β problem gets easier. Find the smallest capacity that works.β