Facebook Pixel

1011. Capacity To Ship Packages Within D Days

Problem Description

You have a conveyor belt with packages that need to be shipped within a certain number of days. Each package has a specific weight given in the weights array, where weights[i] represents the weight of the i-th package.

The shipping process works as follows:

  • You must load packages onto a ship each day in the exact order they appear on the conveyor belt (you cannot skip or rearrange packages)
  • The ship has a weight capacity limit - the total weight of packages loaded on any single day cannot exceed this capacity
  • Once you start loading the next day, you begin with a new empty ship

Your task is to find the minimum weight capacity the ship needs to have so that all packages can be shipped within the given number of days.

For example, if weights = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and days = 5:

  • With a ship capacity of 15, you could ship: Day 1: [1,2,3,4,5], Day 2: [6,7], Day 3: [8], Day 4: [9], Day 5: [10]
  • This would successfully ship all packages within 5 days

The solution uses binary search to find the minimum capacity. The search range is between:

  • Lower bound: The maximum weight among all packages (since the ship must be able to carry at least the heaviest package)
  • Upper bound: The sum of all weights (which would allow shipping everything in one day)

The check function determines if a given capacity allows shipping within the required days by simulating the loading process and counting how many days are needed.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is recognizing that this is an optimization problem where we need to find the minimum value (ship capacity) that satisfies a condition (shipping within the given days). When we need to find a minimum or maximum value that satisfies certain constraints, binary search often comes into play.

Think about the relationship between ship capacity and the number of days needed:

  • If we increase the ship's capacity, we can load more packages per day, reducing the total days needed
  • If we decrease the ship's capacity, we need more days to ship everything
  • This creates a monotonic relationship: higher capacity → fewer days, lower capacity → more days

Since there's this clear relationship, we can use binary search on the capacity. But what should our search range be?

The minimum possible capacity must be at least max(weights) because the ship needs to carry the heaviest package. If the capacity is less than this, we can't ship that package at all.

The maximum capacity we'd ever need is sum(weights), which would allow us to ship everything in a single day. Any capacity higher than this would be wasteful.

Now, for any given capacity in this range, we can check if it's feasible by simulating the shipping process:

  • Start loading packages in order
  • When adding the next package would exceed the capacity, start a new day
  • Count the total days needed
  • If the days needed ≤ the allowed days, this capacity works

Since we want the minimum capacity that works, we binary search for the leftmost position where the condition is satisfied. The solution cleverly uses bisect_left with a key function that returns True when the capacity is sufficient, effectively finding the minimum viable capacity.

Learn more about Binary Search patterns.

Solution Implementation

1class Solution:
2    def shipWithinDays(self, weights: List[int], days: int) -> int:
3        """
4        Find the minimum ship capacity needed to ship all packages within given days.
5
6        Args:
7            weights: List of package weights
8            days: Maximum number of days to ship all packages
9
10        Returns:
11            Minimum ship capacity required
12        """
13
14        def feasible(capacity):
15            """
16            Check if all packages can be shipped within the given days
17            using a ship with the specified capacity.
18            Returns True if capacity is sufficient (feasible), False otherwise.
19            """
20            current_weight = 0
21            days_needed = 1
22
23            for weight in weights:
24                current_weight += weight
25                if current_weight > capacity:
26                    days_needed += 1
27                    current_weight = weight
28
29            return days_needed <= days
30
31        # Binary search bounds
32        left = max(weights)  # Minimum: must carry heaviest package
33        right = sum(weights)  # Maximum: ship everything in one day
34        first_true_index = -1
35
36        # Binary search template: find first capacity where feasible(capacity) is True
37        while left <= right:
38            mid = (left + right) // 2
39            if feasible(mid):
40                first_true_index = mid
41                right = mid - 1  # Search for smaller valid capacity
42            else:
43                left = mid + 1
44
45        return first_true_index
46
1class Solution {
2    public int shipWithinDays(int[] weights, int days) {
3        // Initialize binary search boundaries
4        int left = 0;
5        int right = 0;
6
7        for (int weight : weights) {
8            left = Math.max(left, weight);  // Minimum: must carry heaviest package
9            right += weight;                 // Maximum: ship everything in one day
10        }
11
12        int firstTrueIndex = -1;
13
14        // Binary search template: find first capacity where feasible(capacity) is True
15        while (left <= right) {
16            int mid = left + (right - left) / 2;
17
18            if (feasible(mid, weights, days)) {
19                firstTrueIndex = mid;
20                right = mid - 1;  // Search for smaller valid capacity
21            } else {
22                left = mid + 1;
23            }
24        }
25
26        return firstTrueIndex;
27    }
28
29    private boolean feasible(int capacity, int[] weights, int days) {
30        int currentWeight = 0;
31        int daysNeeded = 1;
32
33        for (int weight : weights) {
34            currentWeight += weight;
35            if (currentWeight > capacity) {
36                currentWeight = weight;
37                daysNeeded++;
38            }
39        }
40
41        return daysNeeded <= days;
42    }
43}
44
1class Solution {
2public:
3    int shipWithinDays(vector<int>& weights, int days) {
4        // Initialize binary search boundaries
5        int left = 0;
6        int right = 0;
7
8        for (const int& weight : weights) {
9            left = max(left, weight);  // Minimum: must carry heaviest package
10            right += weight;            // Maximum: ship everything in one day
11        }
12
13        // Feasible function: check if capacity can ship all packages within days
14        auto feasible = [&](int capacity) -> bool {
15            int currentWeight = 0;
16            int daysNeeded = 1;
17
18            for (const int& weight : weights) {
19                currentWeight += weight;
20                if (currentWeight > capacity) {
21                    currentWeight = weight;
22                    daysNeeded++;
23                }
24            }
25
26            return daysNeeded <= days;
27        };
28
29        int firstTrueIndex = -1;
30
31        // Binary search template: find first capacity where feasible(capacity) is True
32        while (left <= right) {
33            int mid = left + (right - left) / 2;
34
35            if (feasible(mid)) {
36                firstTrueIndex = mid;
37                right = mid - 1;  // Search for smaller valid capacity
38            } else {
39                left = mid + 1;
40            }
41        }
42
43        return firstTrueIndex;
44    }
45};
46
1function shipWithinDays(weights: number[], days: number): number {
2    // Initialize binary search boundaries
3    let left = 0;
4    let right = 0;
5
6    for (const weight of weights) {
7        left = Math.max(left, weight);  // Minimum: must carry heaviest package
8        right += weight;                 // Maximum: ship everything in one day
9    }
10
11    // Feasible function: check if capacity can ship all packages within days
12    const feasible = (capacity: number): boolean => {
13        let currentWeight = 0;
14        let daysNeeded = 1;
15
16        for (const weight of weights) {
17            currentWeight += weight;
18            if (currentWeight > capacity) {
19                currentWeight = weight;
20                daysNeeded++;
21            }
22        }
23
24        return daysNeeded <= days;
25    };
26
27    let firstTrueIndex = -1;
28
29    // Binary search template: find first capacity where feasible(capacity) is True
30    while (left <= right) {
31        const mid = Math.floor((left + right) / 2);
32
33        if (feasible(mid)) {
34            firstTrueIndex = mid;
35            right = mid - 1;  // Search for smaller valid capacity
36        } else {
37            left = mid + 1;
38        }
39    }
40
41    return firstTrueIndex;
42}
43

Solution Approach

The solution implements a binary search algorithm to find the minimum ship capacity. Here's how the implementation works:

Binary Search Setup:

  • left = max(weights): The minimum possible capacity (must carry the heaviest package)
  • right = sum(weights) + 1: One more than the maximum needed capacity (exclusive upper bound for the search range)

The Check Function: The check(mx) function determines if a given capacity mx is sufficient to ship all packages within the allowed days:

def check(mx):
    ws, cnt = 0, 1  # ws: current weight sum, cnt: days used (start with day 1)
    for w in weights:
        ws += w
        if ws > mx:  # Current load exceeds capacity
            cnt += 1  # Start a new day
            ws = w    # Reset weight sum to current package
    return cnt <= days  # Check if we can ship within allowed days

The function simulates the loading process:

  • ws tracks the cumulative weight loaded on the current day
  • cnt counts the number of days used (initialized to 1 since we start on day 1)
  • For each package, we try to add it to the current day's load
  • If adding it would exceed capacity mx, we start a new day and put this package as the first item of the new day
  • Finally, we return True if the total days needed doesn't exceed the limit

Binary Search Execution:

return left + bisect_left(range(left, right), True, key=check)

This line uses Python's bisect_left function cleverly:

  • range(left, right) creates the search space of possible capacities
  • key=check transforms each capacity value to a boolean (True if sufficient, False if not)
  • Since larger capacities will return True and smaller ones False, we get a pattern like: [False, False, ..., False, True, True, ..., True]
  • bisect_left finds the leftmost position where True would be inserted, which is the minimum sufficient capacity
  • Adding left to the result gives us the actual capacity value since bisect_left returns an index

The time complexity is O(n * log(sum(weights) - max(weights))) where n is the length of the weights array, as we perform a binary search and each check operation takes O(n) time.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example with weights = [3, 2, 2, 4, 1, 4] and days = 3.

Step 1: Determine search boundaries

  • left = max(weights) = 4 (ship must carry the heaviest package)
  • right = sum(weights) + 1 = 16 + 1 = 17 (upper bound for search)

Step 2: Binary search for minimum capacity

We'll test different capacities to find the minimum that works:

Testing capacity = 4:

  • Day 1: Load [3] → total = 3 ✓
    • Try adding 2 → total would be 5 > 4, so start new day
  • Day 2: Load [2, 2] → total = 4 ✓
    • Try adding 4 → total would be 8 > 4, so start new day
  • Day 3: Load [4] → total = 4 ✓
    • Try adding 1 → total would be 5 > 4, so start new day
  • Day 4: Load [1] → total = 1 ✓
    • Try adding 4 → total would be 5 > 4, so start new day
  • Day 5: Load [4] → total = 4 ✓

Result: Need 5 days > 3 allowed days ✗

Testing capacity = 6:

  • Day 1: Load [3, 2] → total = 5 ✓
    • Try adding 2 → total would be 7 > 6, so start new day
  • Day 2: Load [2, 4] → total = 6 ✓
    • Try adding 1 → total would be 7 > 6, so start new day
  • Day 3: Load [1, 4] → total = 5 ✓

Result: Need 3 days = 3 allowed days ✓

Binary search process: The actual binary search would proceed as:

  • Search range: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
  • Check results: [F, F, T, T, T, T, T, T, T, T, T, T, T]
  • bisect_left finds the first True at index 2
  • Minimum capacity = 4 + 2 = 6

Final shipping arrangement with capacity 6:

  • Day 1: [3, 2] (weight: 5)
  • Day 2: [2, 4] (weight: 6)
  • Day 3: [1, 4] (weight: 5)

The minimum ship capacity needed is 6.

Time and Space Complexity

Time Complexity: O(n * log(sum(weights) - max(weights)))

The algorithm uses binary search on the range [max(weights), sum(weights)]. The binary search space has a size of sum(weights) - max(weights) + 1, which requires O(log(sum(weights) - max(weights))) iterations.

For each binary search iteration, the check function is called, which iterates through all n elements in the weights array once, taking O(n) time.

Therefore, the overall time complexity is O(n * log(sum(weights) - max(weights))).

Space Complexity: O(1)

The algorithm uses only a constant amount of extra space:

  • Variables left and right for binary search boundaries
  • Variables ws and cnt in the check function
  • The check function itself doesn't use recursion or additional data structures

The range(left, right) in bisect_left is a generator in Python 3, which doesn't create the entire list in memory, so it doesn't affect space complexity.

Therefore, the space complexity is O(1).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Using Wrong Binary Search Template Variant

A common mistake is using while left < right with right = mid, which is a different binary search variant:

# WRONG - different template variant
while left < right:
    mid = (left + right) // 2
    if feasible(mid):
        right = mid
    else:
        left = mid + 1
return left

Correct template:

first_true_index = -1
while left <= right:
    mid = (left + right) // 2
    if feasible(mid):
        first_true_index = mid
        right = mid - 1
    else:
        left = mid + 1
return first_true_index

The template explicitly tracks the answer with first_true_index, uses while left <= right, and always moves right = mid - 1 when the condition is true.

2. Off-by-One Error in Day Counting Logic

A frequent mistake occurs when checking if the current load exceeds capacity:

Incorrect Implementation:

def feasible(capacity):
    current_weight = 0
    days_needed = 1

    for weight in weights:
        if current_weight + weight > capacity:
            days_needed += 1
            current_weight = weight
        else:
            current_weight += weight

    return days_needed <= days

Correct Implementation:

def feasible(capacity):
    current_weight = 0
    days_needed = 1

    for weight in weights:
        current_weight += weight
        if current_weight > capacity:
            days_needed += 1
            current_weight = weight

    return days_needed <= days

By adding first and then checking, the logic is simpler: if capacity is exceeded, the current package starts a new day.

3. Incorrect Binary Search Bounds

Setting the lower bound to 1 or 0 instead of max(weights):

# WRONG
left = 1  # or 0

# CORRECT
left = max(weights)

The ship must carry at least the heaviest package.

4. Forgetting to Initialize Day Count to 1

# WRONG
days_needed = 0

# CORRECT
days_needed = 1

We begin shipping on day 1, not day 0.

Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What are the two properties the problem needs to have for dynamic programming to be applicable? (Select 2)


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More