Minimum Number of Water Refill Stops

Last Updated : 6 Jul, 2026

Given an integer dist representing the distance to the destination, an integer p representing the initial amount of water, and a 2D array arr[][], where arr[i] = [positioni, wateri] means the ith checkpoint is positioni miles from the start and holds wateri liters of water.

  • Geek starts from position 0 with p liters of water, has unlimited carrying capacity, and consumes 1 liter of water for every mile traveled.
  • At each checkpoint, Geek may collect all the available water.

Return the minimum number of refill stops required to reach the destination. If it is not possible to reach the destination, return -1.

Note: Geek may arrive at a checkpoint or the destination with exactly 0 liters of water.

Examples:

Input: dist = 100, p = 10, arr[][] = [[10,60],[20,30],[30,30],[60,40]]
Output: 2
Explanation:
Start with 10L, walk to position 10 (uses 10L), arrive with 0L.
Stop 1: collect 60L at position 10.
Walk through positions 20, 30, and 60 without stopping again until water runs low.
Stop 2: collect 40L at position 60, enough to reach the destination.

Input: dist = 100, p = 1, arr[][] = [[10,100]]
Output: -1
Explanation: With only 1L of water, Geek cannot cover the 10 miles to reach even the first checkpoint.

[Naive Approach] Exponential Recursion (Skip or Stop) - O(2^n) Time and O(n) Space

At each checkpoint, Geek either skips it or stops to collect its water. Exploring both choices recursively checks every possible combination of stops, and the minimum among all successful paths is the answer. However, the number of combinations doubles at every checkpoint, giving an exponential O(2^n) time complexity, making this approach infeasible even for a few dozen checkpoints.

Consider dist = 100, p = 10, and arr[][] = {{10, 60}, {20, 30}, {30, 30}, {60, 40}}.

  • At checkpoint 10, Geek uses all 10 liters of water to reach it. If this checkpoint is skipped, Geek cannot reach checkpoint 20, so the only valid choice is to stop and collect 60 liters.
  • At checkpoint 20, Geek has enough water to continue. Therefore, both possibilities are explored recursively: skip the checkpoint or stop and collect 30 liters.
  • The same two choices are considered at every remaining checkpoint, generating all possible combinations of refill stops.
  • Among all valid paths, the path that refills only at checkpoints 10 and 60 reaches the destination using 2 stops, which is the minimum possible.
C++
#include <bits/stdc++.h>
using namespace std;

int ans;

void dfs(int i, int pos, int water, int cnt, int dist, vector<vector<int>> &arr)
{
    // Destination reached.
    if (pos + water >= dist)
    {
        ans = min(ans, cnt);
        return;
    }

    // No more checkpoints left to consider.
    if (i == (int)arr.size())
        return;

    int nextPos = arr[i][0];
    int w = arr[i][1];

    // Cannot reach this checkpoint.
    if (pos + water < nextPos)
        return;

    int rem = water - (nextPos - pos);

    // Branch 1: skip this checkpoint.
    dfs(i + 1, nextPos, rem, cnt, dist, arr);

    // Branch 2: stop and collect its water.
    dfs(i + 1, nextPos, rem + w, cnt + 1, dist, arr);
}

int minimumStops(int dist, int p, vector<vector<int>> &arr)
{
    ans = INT_MAX;

    dfs(0, 0, p, 0, dist, arr);

    return (ans == INT_MAX) ? -1 : ans;
}

int main()
{
    int dest = 100;
    int p = 10;
    vector<vector<int>> arr = {{10, 60}, {20, 30}, {30, 30}, {60, 40}};

    cout << minimumStops(100, 10, arr) << endl;

    return 0;
}
Java
public class GFG {

    static int ans;

    static void dfs(int i, int pos, int water, int cnt, int dist, int[][] arr) {

        // Destination reached.
        if (pos + water >= dist) {
            ans = Math.min(ans, cnt);
            return;
        }

        // No more checkpoints left to consider.
        if (i == arr.length)
            return;

        int nextPos = arr[i][0];
        int w = arr[i][1];

        // Cannot reach this checkpoint.
        if (pos + water < nextPos)
            return;

        int rem = water - (nextPos - pos);

        // Branch 1: skip this checkpoint.
        dfs(i + 1, nextPos, rem, cnt, dist, arr);

        // Branch 2: stop and collect its water.
        dfs(i + 1, nextPos, rem + w, cnt + 1, dist, arr);
    }

    static int minimumStops(int dist, int p, int[][] arr) {
        ans = Integer.MAX_VALUE;

        dfs(0, 0, p, 0, dist, arr);

        return (ans == Integer.MAX_VALUE) ? -1 : ans;
    }

    public static void main(String[] args) {
        int dest = 100;
        int p = 10;
        int[][] arr = {
            {10, 60},
            {20, 30},
            {30, 30},
            {60, 40}
        };

        System.out.println(minimumStops(dest, p, arr));
    }
}
Python
ans = float('inf')


def dfs(i, pos, water, cnt, dist, arr):
    global ans

    # Destination reached.
    if pos + water >= dist:
        ans = min(ans, cnt)
        return

    # No more checkpoints left to consider.
    if i == len(arr):
        return

    nextPos = arr[i][0]
    w = arr[i][1]

    # Cannot reach this checkpoint.
    if pos + water < nextPos:
        return

    rem = water - (nextPos - pos)

    # Branch 1: skip this checkpoint.
    dfs(i + 1, nextPos, rem, cnt, dist, arr)

    # Branch 2: stop and collect its water.
    dfs(i + 1, nextPos, rem + w, cnt + 1, dist, arr)


def minimumStops(dist, p, arr):
    global ans

    ans = float('inf')

    dfs(0, 0, p, 0, dist, arr)

    return -1 if ans == float('inf') else ans


if __name__ == "__main__":
    dest = 100
    p = 10
    arr = [
        [10, 60],
        [20, 30],
        [30, 30],
        [60, 40]
    ]

    print(minimumStops(dest, p, arr))
C#
using System;

class GFG {

    static int ans;

    static void dfs(int i, int pos, int water, int cnt, int dist, int[,] arr) {

        // Destination reached.
        if (pos + water >= dist) {
            ans = Math.Min(ans, cnt);
            return;
        }

        // No more checkpoints left to consider.
        if (i == arr.GetLength(0))
            return;

        int nextPos = arr[i, 0];
        int w = arr[i, 1];

        // Cannot reach this checkpoint.
        if (pos + water < nextPos)
            return;

        int rem = water - (nextPos - pos);

        // Branch 1: skip this checkpoint.
        dfs(i + 1, nextPos, rem, cnt, dist, arr);

        // Branch 2: stop and collect its water.
        dfs(i + 1, nextPos, rem + w, cnt + 1, dist, arr);
    }

    static int minimumStops(int dist, int p, int[,] arr) {
        ans = int.MaxValue;

        dfs(0, 0, p, 0, dist, arr);

        return (ans == int.MaxValue) ? -1 : ans;
    }

    static void Main() {
        int dest = 100;
        int p = 10;
        int[,] arr = {
            {10, 60},
            {20, 30},
            {30, 30},
            {60, 40}
        };

        Console.WriteLine(minimumStops(dest, p, arr));
    }
}
JavaScript
function minimumStops(dist, p, arr) {
    let ans = Number.MAX_SAFE_INTEGER;

    function dfs(i, pos, water, cnt) {

        // Destination reached.
        if (pos + water >= dist) {
            ans = Math.min(ans, cnt);
            return;
        }

        // No more checkpoints left to consider.
        if (i === arr.length)
            return;

        const nextPos = arr[i][0];
        const w = arr[i][1];

        // Cannot reach this checkpoint.
        if (pos + water < nextPos)
            return;

        const rem = water - (nextPos - pos);

        // Branch 1: skip this checkpoint.
        dfs(i + 1, nextPos, rem, cnt);

        // Branch 2: stop and collect its water.
        dfs(i + 1, nextPos, rem + w, cnt + 1);
    }

    dfs(0, 0, p, 0);

    return (ans === Number.MAX_SAFE_INTEGER) ? -1 : ans;
}

// Driver code
    const dest = 100;
    const p = 10;
    const arr = [
        [10, 60],
        [20, 30],
        [30, 30],
        [60, 40]
    ];

    console.log(minimumStops(dest, p, arr));

Output
2

[Expected Approach] Greedy with Max-Heap - O(n log n) Time and O(n) Space

The idea is to delay refilling until it becomes necessary. While moving forward, store the water available at every passed checkpoint in a max-heap. Whenever the current water is insufficient to reach the next checkpoint (or the destination), refill using the largest available amount from the heap.

Choosing the largest available refill always gives the maximum possible remaining water while using only one stop, thereby minimizing the total number of refill stops.

Consider dist = 100, p = 10, and arr[][] = {{10, 60}, {20, 30}, {30, 30}, {60, 40}}.

  • Travel from 0 to 10, using all 10 liters of water. Push 60 into the max-heap.
  • To reach checkpoint 20, 10 more liters are needed but no water remains. Refill with the largest available amount (60) from the heap (1st stop). After reaching 20, 50 liters remain. Push 30 into the heap.
  • Travel to checkpoint 30, leaving 40 liters. Push another 30 into the heap.
  • Travel to checkpoint 60, leaving 10 liters. Push 40 into the heap.
  • To reach the destination at 100, 40 liters are needed but only 10 remain. Refill with the largest available amount (40) from the heap (2nd stop), giving enough water to reach the destination.

Thus, the minimum number of refill stops required is 2.

C++
#include <iostream>
#include <queue>
#include <vector>
using namespace std;

int minimumStops(int dist, int p, vector<vector<int>> &arr)
{
    priority_queue<int> pq;

    long long water = p;
    int prev = 0;
    int stops = 0;

    // Process all checkpoints and finally the destination.
    for (int i = 0; i <= arr.size(); i++)
    {
        int pos = (i == arr.size()) ? dist : arr[i][0];
        int w = (i == arr.size()) ? 0 : arr[i][1];

        // Consume water to reach the current position.
        water -= (pos - prev);

        // Refill using the largest available checkpoint.
        while (water < 0 && !pq.empty())
        {
            water += pq.top();
            pq.pop();
            stops++;
        }

        // Destination cannot be reached.
        if (water < 0)
        {
            return -1;
        }

        // Store the current checkpoint for future refills.
        pq.push(w);
        prev = pos;
    }

    return stops;
}

int main()
{
    int dist = 100;
    int p = 10;
    vector<vector<int>> arr = {{10, 60}, {20, 30}, {30, 30}, {60, 40}};

    cout << minimumStops(100, 10, arr) << endl;

    return 0;
}
Java
import java.util.Collections;
import java.util.PriorityQueue;

public class GFG {

    static int minimumStops(int dist, int p, int[][] arr) {
        PriorityQueue<Integer> pq = new PriorityQueue<>(Collections.reverseOrder());

        long water = p;
        int prev = 0;
        int stops = 0;

        // Process all checkpoints and finally the destination.
        for (int i = 0; i <= arr.length; i++) {
            int pos = (i == arr.length) ? dist : arr[i][0];
            int w = (i == arr.length) ? 0 : arr[i][1];

            // Consume water to reach the current position.
            water -= (pos - prev);

            // Refill using the largest available checkpoint.
            while (water < 0 && !pq.isEmpty()) {
                water += pq.poll();
                stops++;
            }

            // Destination cannot be reached.
            if (water < 0) {
                return -1;
            }

            // Store the current checkpoint for future refills.
            pq.offer(w);
            prev = pos;
        }

        return stops;
    }

    public static void main(String[] args) {
        int dist = 100;
        int p = 10;
        int[][] arr = {
            {10, 60},
            {20, 30},
            {30, 30},
            {60, 40}
        };

        System.out.println(minimumStops(dist, p, arr));
    }
}
Python
import heapq

def minimumStops(dist, p, arr):
    pq = []

    water = p
    prev = 0
    stops = 0

    # Process all checkpoints and finally the destination.
    for i in range(len(arr) + 1):
        pos = dist if i == len(arr) else arr[i][0]
        w = 0 if i == len(arr) else arr[i][1]

        # Consume water to reach the current position.
        water -= (pos - prev)

        # Refill using the largest available checkpoint.
        while water < 0 and pq:
            water += -heapq.heappop(pq)
            stops += 1

        # Destination cannot be reached.
        if water < 0:
            return -1

        # Store the current checkpoint for future refills.
        heapq.heappush(pq, -w)
        prev = pos

    return stops


if __name__ == "__main__":
    dist = 100
    p = 10
    arr = [
        [10, 60],
        [20, 30],
        [30, 30],
        [60, 40]
    ]

    print(minimumStops(dist, p, arr))
C#
using System;
using System.Collections.Generic;

class GFG {

    static int minimumStops(int dist, int p, int[,] arr) {
        PriorityQueue<int, int> pq = new PriorityQueue<int, int>();

        long water = p;
        int prev = 0;
        int stops = 0;

        // Process all checkpoints and finally the destination.
        for (int i = 0; i <= arr.GetLength(0); i++) {
            int pos = (i == arr.GetLength(0)) ? dist : arr[i, 0];
            int w = (i == arr.GetLength(0)) ? 0 : arr[i, 1];

            // Consume water to reach the current position.
            water -= (pos - prev);

            // Refill using the largest available checkpoint.
            while (water < 0 && pq.Count > 0) {
                water += pq.Dequeue();
                stops++;
            }

            // Destination cannot be reached.
            if (water < 0) {
                return -1;
            }

            // Store the current checkpoint for future refills.
            pq.Enqueue(w, -w);
            prev = pos;
        }

        return stops;
    }

    static void Main() {
        int dist = 100;
        int p = 10;
        int[,] arr = {
            {10, 60},
            {20, 30},
            {30, 30},
            {60, 40}
        };

        Console.WriteLine(minimumStops(dist, p, arr));
    }
}
JavaScript
class MaxHeap {
    constructor() {
        this.heap = [];
    }

    push(val) {
        this.heap.push(val);

        let i = this.heap.length - 1;

        while (i > 0) {
            let p = Math.floor((i - 1) / 2);

            if (this.heap[p] >= this.heap[i]) {
                break;
            }

            [this.heap[p], this.heap[i]] = [this.heap[i], this.heap[p]];
            i = p;
        }
    }

    pop() {
        if (this.heap.length === 1) {
            return this.heap.pop();
        }

        let top = this.heap[0];
        this.heap[0] = this.heap.pop();

        let i = 0;

        while (true) {
            let l = 2 * i + 1;
            let r = 2 * i + 2;
            let largest = i;

            if (l < this.heap.length && this.heap[l] > this.heap[largest]) {
                largest = l;
            }

            if (r < this.heap.length && this.heap[r] > this.heap[largest]) {
                largest = r;
            }

            if (largest === i) {
                break;
            }

            [this.heap[i], this.heap[largest]] = [this.heap[largest], this.heap[i]];
            i = largest;
        }

        return top;
    }

    isEmpty() {
        return this.heap.length === 0;
    }
}

function minimumStops(dist, p, arr) {
    const pq = new MaxHeap();

    let water = p;
    let prev = 0;
    let stops = 0;

    // Process all checkpoints and finally the destination.
    for (let i = 0; i <= arr.length; i++) {
        const pos = (i === arr.length) ? dist : arr[i][0];
        const w = (i === arr.length) ? 0 : arr[i][1];

        // Consume water to reach the current position.
        water -= (pos - prev);

        // Refill using the largest available checkpoint.
        while (water < 0 && !pq.isEmpty()) {
            water += pq.pop();
            stops++;
        }

        // Destination cannot be reached.
        if (water < 0) {
            return -1;
        }

        // Store the current checkpoint for future refills.
        pq.push(w);
        prev = pos;
    }

    return stops;
}

// Driver code
const dist = 100;
const p = 10;
const arr = [
    [10, 60],
    [20, 30],
    [30, 30],
    [60, 40]
];

console.log(minimumStops(dist, p, arr));

Output
2
Comment