Facebook Pixel

2105. Watering Plants II

Problem Description

Alice and Bob need to water n plants arranged in a row, numbered from 0 to n - 1. Each plant requires a specific amount of water given in the array plants[i].

Initial Setup:

  • Alice starts with a full watering can of capacity capacityA
  • Bob starts with a full watering can of capacity capacityB

Watering Rules:

  • Alice waters plants from left to right (starting at plant 0)
  • Bob waters plants from right to left (starting at plant n-1)
  • They water simultaneously and move at the same pace
  • A person must water a plant if they have enough water in their can
  • If they don't have enough water, they refill first (instantaneously), then water the plant
  • When both meet at the same plant:
    • The person with more water in their can waters it
    • If they have equal amounts, Alice waters it

Goal: Find the minimum total number of refills needed by both Alice and Bob to water all plants.

Example: If plants = [2, 2, 3, 3], capacityA = 5, capacityB = 5:

  • Alice waters plant 0 (needs 2), has 3 left
  • Bob waters plant 3 (needs 3), has 2 left
  • Alice waters plant 1 (needs 2), has 1 left
  • Bob waters plant 2 (needs 3), but only has 2, so refills first (1 refill), then waters
  • Total refills: 1
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Since Alice and Bob water plants from opposite ends moving toward each other, this naturally suggests a two-pointer approach. We can use pointer i for Alice starting at position 0, and pointer j for Bob starting at position n-1.

The key insight is that we can simulate the watering process directly. As they move toward the center simultaneously, we track:

  • How much water each person currently has in their can
  • When they need to refill (when current water < plant's requirement)

For each step:

  1. Check if Alice has enough water for plants[i]. If not, refill and increment the refill counter
  2. Water the plant and decrease Alice's water by plants[i]
  3. Do the same for Bob with plants[j]
  4. Move both pointers inward (i++, j--)

The simulation continues while i < j. When the pointers meet (i == j), there's one plant left that both could potentially water. According to the rules:

  • The person with more water should water it
  • We only need a refill if even the person with more water doesn't have enough

This is why the final check is max(a, b) < plants[i] when i == j. If true, one more refill is needed.

The beauty of this approach is its simplicity - we're literally following the problem's rules step by step, making it easy to implement and verify correctness. The time complexity is O(n) since we visit each plant exactly once, and space complexity is O(1) as we only use a few variables to track the state.

Learn more about Two Pointers patterns.

Solution Approach

We implement the two-pointer simulation approach to solve this problem:

Initialize Variables:

  • a = capacityA and b = capacityB to track current water amounts for Alice and Bob
  • ans = 0 to count total refills
  • i = 0 and j = len(plants) - 1 as pointers for Alice and Bob's positions

Main Simulation Loop (while i < j):

For each iteration, we handle both Alice and Bob:

  1. Alice's turn at position i:

    • Check if a < plants[i] (not enough water)
    • If true: refill (ans += 1 and a = capacityA)
    • Water the plant: a -= plants[i]
  2. Bob's turn at position j:

    • Check if b < plants[j] (not enough water)
    • If true: refill (ans += 1 and b = capacityB)
    • Water the plant: b -= plants[j]
  3. Move pointers inward:

    • i = i + 1 and j = j - 1

Handle the Middle Plant (when i == j):

After the loop, if there's a middle plant (odd number of plants), we need to determine who waters it:

  • The person with more water should water it: max(a, b)
  • If even the person with more water doesn't have enough (max(a, b) < plants[i]), add one more refill

This is handled elegantly in one line:

ans += i == j and max(a, b) < plants[i]

The expression evaluates to 1 (True) only when both conditions are met, adding exactly one refill when needed.

Time Complexity: O(n) - we visit each plant exactly once Space Complexity: O(1) - only using a constant amount of extra variables

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 trace through a small example to illustrate the solution approach.

Input: plants = [3, 1, 2, 4, 1], capacityA = 6, capacityB = 5

Initial State:

  • Alice: position i = 0, water a = 6
  • Bob: position j = 4, water b = 5
  • Refills: ans = 0

Step 1: (i = 0, j = 4)

  • Alice waters plant 0 (needs 3): a = 6 - 3 = 3
  • Bob waters plant 4 (needs 1): b = 5 - 1 = 4
  • Move pointers: i = 1, j = 3

Step 2: (i = 1, j = 3)

  • Alice waters plant 1 (needs 1): a = 3 - 1 = 2
  • Bob waters plant 3 (needs 4): Has 4 water, exactly enough! b = 4 - 4 = 0
  • Move pointers: i = 2, j = 2

Step 3: Loop ends since i == j

  • Both Alice and Bob are at plant 2 (needs 2 water)
  • Alice has 2 water, Bob has 0 water
  • Since max(2, 0) = 2 and plant needs 2, Alice has exactly enough water
  • No refill needed

Result: Total refills = 0


Another Example with Refills:

Input: plants = [4, 2, 1, 5], capacityA = 4, capacityB = 3

Step 1: (i = 0, j = 3)

  • Alice waters plant 0 (needs 4): a = 4 - 4 = 0
  • Bob checks plant 3 (needs 5): Has 3 water, not enough! Refill (ans = 1), then water: b = 3 - 5 = -2 β†’ Actually b = capacityB - 5 = 3 - 5 is wrong. After refill: b = 3, then water: b = 3 - 5... Wait, capacity is only 3 but plant needs 5!

Let me fix this example:

Input: plants = [2, 1, 1, 2], capacityA = 3, capacityB = 2

Step 1: (i = 0, j = 3)

  • Alice waters plant 0 (needs 2): a = 3 - 2 = 1
  • Bob checks plant 3 (needs 2): Has 2 water, exactly enough! b = 2 - 2 = 0
  • Move pointers: i = 1, j = 2

Step 2: (i = 1, j = 2)

  • Alice waters plant 1 (needs 1): a = 1 - 1 = 0
  • Bob checks plant 2 (needs 1): Has 0 water, not enough! Refill (ans = 1), then water: b = 2 - 1 = 1
  • Move pointers: i = 2, j = 1

Step 3: Loop ends since i > j

Result: Total refills = 1

Solution Implementation

1class Solution:
2    def minimumRefill(self, plants: List[int], capacityA: int, capacityB: int) -> int:
3        # Initialize current water amounts for Alice and Bob
4        current_water_a = capacityA
5        current_water_b = capacityB
6      
7        # Counter for number of refills
8        refill_count = 0
9      
10        # Two pointers: Alice starts from left, Bob from right
11        left = 0
12        right = len(plants) - 1
13      
14        # Process plants while pointers haven't crossed
15        while left < right:
16            # Alice waters the plant at left position
17            if current_water_a < plants[left]:
18                # Not enough water, need to refill
19                refill_count += 1
20                current_water_a = capacityA
21            current_water_a -= plants[left]
22          
23            # Bob waters the plant at right position
24            if current_water_b < plants[right]:
25                # Not enough water, need to refill
26                refill_count += 1
27                current_water_b = capacityB
28            current_water_b -= plants[right]
29          
30            # Move pointers inward
31            left += 1
32            right -= 1
33      
34        # Handle the middle plant if odd number of plants
35        # The person with more water will water it
36        if left == right and max(current_water_a, current_water_b) < plants[left]:
37            refill_count += 1
38      
39        return refill_count
40
1class Solution {
2    public int minimumRefill(int[] plants, int capacityA, int capacityB) {
3        // Track current water amounts for Alice and Bob
4        int currentWaterA = capacityA;
5        int currentWaterB = capacityB;
6      
7        // Count total number of refills needed
8        int refillCount = 0;
9      
10        // Two pointers: Alice starts from left, Bob starts from right
11        int left = 0;
12        int right = plants.length - 1;
13      
14        // Process plants until pointers meet
15        while (left < right) {
16            // Alice waters the plant at left position
17            if (currentWaterA < plants[left]) {
18                // Not enough water, need to refill
19                refillCount++;
20                currentWaterA = capacityA;
21            }
22            currentWaterA -= plants[left];
23          
24            // Bob waters the plant at right position
25            if (currentWaterB < plants[right]) {
26                // Not enough water, need to refill
27                refillCount++;
28                currentWaterB = capacityB;
29            }
30            currentWaterB -= plants[right];
31          
32            // Move pointers towards center
33            left++;
34            right--;
35        }
36      
37        // Handle the middle plant when array has odd length
38        // The person with more water will water it
39        if (left == right) {
40            // Check if the person with more water needs a refill
41            int maxWater = Math.max(currentWaterA, currentWaterB);
42            if (maxWater < plants[left]) {
43                refillCount++;
44            }
45        }
46      
47        return refillCount;
48    }
49}
50
1class Solution {
2public:
3    int minimumRefill(vector<int>& plants, int capacityA, int capacityB) {
4        // Initialize current water amounts for both watering cans
5        int currentWaterA = capacityA;
6        int currentWaterB = capacityB;
7        int refillCount = 0;
8      
9        // Two pointers: Alice starts from left, Bob starts from right
10        int left = 0;
11        int right = plants.size() - 1;
12      
13        // Process plants from both ends until pointers meet
14        while (left < right) {
15            // Alice waters the plant at left position
16            if (currentWaterA < plants[left]) {
17                // Not enough water, refill Alice's can
18                refillCount++;
19                currentWaterA = capacityA;
20            }
21            currentWaterA -= plants[left];
22          
23            // Bob waters the plant at right position
24            if (currentWaterB < plants[right]) {
25                // Not enough water, refill Bob's can
26                refillCount++;
27                currentWaterB = capacityB;
28            }
29            currentWaterB -= plants[right];
30          
31            // Move pointers towards center
32            left++;
33            right--;
34        }
35      
36        // Handle the middle plant if there's an odd number of plants
37        // The person with more water handles it, check if refill needed
38        if (left == right && max(currentWaterA, currentWaterB) < plants[left]) {
39            refillCount++;
40        }
41      
42        return refillCount;
43    }
44};
45
1/**
2 * Calculates the minimum number of refills needed for two people watering plants from opposite ends
3 * @param plants - Array where plants[i] represents the water needed for plant i
4 * @param capacityA - The water capacity of person A's watering can
5 * @param capacityB - The water capacity of person B's watering can
6 * @returns The minimum number of refills needed
7 */
8function minimumRefill(plants: number[], capacityA: number, capacityB: number): number {
9    // Current water amounts for person A and B
10    let currentWaterA: number = capacityA;
11    let currentWaterB: number = capacityB;
12  
13    // Counter for number of refills
14    let refillCount: number = 0;
15  
16    // Person A starts from the left, person B starts from the right
17    let leftIndex: number = 0;
18    let rightIndex: number = plants.length - 1;
19  
20    // Process plants until the two people meet
21    while (leftIndex < rightIndex) {
22        // Person A waters plant from the left
23        if (currentWaterA < plants[leftIndex]) {
24            // Not enough water, need to refill
25            refillCount++;
26            currentWaterA = capacityA;
27        }
28        currentWaterA -= plants[leftIndex];
29      
30        // Person B waters plant from the right
31        if (currentWaterB < plants[rightIndex]) {
32            // Not enough water, need to refill
33            refillCount++;
34            currentWaterB = capacityB;
35        }
36        currentWaterB -= plants[rightIndex];
37      
38        // Move both pointers towards the center
39        leftIndex++;
40        rightIndex--;
41    }
42  
43    // Handle the middle plant if there's an odd number of plants
44    // The person with more water will water it
45    if (leftIndex === rightIndex) {
46        const maxWater: number = Math.max(currentWaterA, currentWaterB);
47        if (maxWater < plants[leftIndex]) {
48            refillCount++;
49        }
50    }
51  
52    return refillCount;
53}
54

Time and Space Complexity

Time Complexity: O(n), where n is the length of the plants array.

The algorithm uses a two-pointer approach where pointer i starts from the beginning (index 0) and pointer j starts from the end (index len(plants) - 1). In each iteration of the while loop, both pointers move one step closer to each other (i increments and j decrements). The loop continues while i < j, meaning each element in the array is visited exactly once. After the loop, there's a constant-time check for the middle element when the array has odd length. Therefore, the total number of operations is proportional to the number of elements in the array, resulting in O(n) time complexity.

Space Complexity: O(1)

The algorithm only uses a fixed amount of extra space regardless of the input size. The variables used are:

  • a and b for tracking current water amounts
  • ans for counting refills
  • i and j as pointers
  • No additional data structures are created

Since the number of variables remains constant and doesn't scale with the input size, the space complexity is O(1).

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

Common Pitfalls

1. Incorrect Middle Plant Handling

Pitfall: A common mistake is forgetting to check if there's actually a middle plant when the number of plants is odd, or incorrectly determining who should water it.

Incorrect Implementation:

# Wrong: Always checking for middle plant even when n is even
if max(current_water_a, current_water_b) < plants[left]:
    refill_count += 1

# Wrong: Not considering who has more water
if current_water_a < plants[left]:
    refill_count += 1

Solution: Always verify that left == right after the loop to confirm there's a middle plant, and use the maximum of both water amounts to determine if a refill is needed:

if left == right and max(current_water_a, current_water_b) < plants[left]:
    refill_count += 1

2. Refilling After Watering Instead of Before

Pitfall: Checking for refill after attempting to water the plant, which would result in negative water amounts.

Incorrect Implementation:

# Wrong order - watering before checking capacity
current_water_a -= plants[left]
if current_water_a < 0:  # Now it's too late!
    refill_count += 1
    current_water_a = capacityA

Solution: Always check if refill is needed BEFORE watering:

if current_water_a < plants[left]:
    refill_count += 1
    current_water_a = capacityA
current_water_a -= plants[left]

3. Not Resetting to Full Capacity After Refill

Pitfall: Adding the capacity to current water instead of resetting to full capacity.

Incorrect Implementation:

if current_water_a < plants[left]:
    refill_count += 1
    current_water_a += capacityA  # Wrong! This adds to existing water

Solution: Reset to full capacity when refilling:

if current_water_a < plants[left]:
    refill_count += 1
    current_water_a = capacityA  # Correct: reset to full capacity

4. Off-by-One Errors with Pointers

Pitfall: Using wrong loop conditions or pointer updates that might skip plants or cause index out of bounds.

Incorrect Implementation:

# Wrong: using <= might process middle plant twice
while left <= right:
    # process both left and right
    left += 1
    right -= 1

Solution: Use left < right for the main loop and handle the middle plant separately:

while left < right:
    # process both left and right
    left += 1
    right -= 1

# Handle middle plant separately if it exists
if left == right:
    # handle middle plant
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How does quick sort divide the problem into subproblems?


Recommended Readings

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

Load More