Open In App

Water Jug problem using BFS

Last Updated : 14 Sep, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Given two empty jugs of m and n litres respectively. The jugs don't have markings to allow measuring smaller quantities. You have to use the jugs to measure d litres of water. The task is to find the minimum number of operations to be performed to obtain d litres of water in one of the jugs. In case of no solution exist, return -1.

The operations you can perform are:

  • Empty a Jug
  • Fill a Jug
  • Pour water from one jug to the other until one of the jugs is either empty or full.

Example:

Input: m = 3, n = 5, d = 4
Output: 6
Explanation: Operations are as follow:

  • Initially, both jugs are empty (jug1 = 0, jug2 = 0).
  • Step 1: Fill the 5 liter jug -> (0, 5).
  • Step 2: Pour from the 5 liter jug to the 3 liter jug -> (3, 2).
  • Step 3: Empty the 3 liter jug -> (0, 2).
  • Step 4: Pour the 2 liters from the 5-liter jug to the 3 liter jug -> (2, 0).
  • Step 5: Fill the 5 liter jug again -> (2, 5).
  • Step 6: Pour 1 liter from the 5 liter jug into the 3 liter jug -> (3, 4).

Now, the 5 liter jug contains exactly 4 liters, so we stop and return 6 steps.

Input: m = 8, n = 56, d = 46
Output: -1
Explanation: Not possible to fill any one of the jug with 46 litre of water.

Water Jug Puzzle has many variations. We have discussed the optimal solution in Minimum Steps for Two Water Jug Problem.. In this post, a BFS based solution is discussed.

Approach:

To solve this problem, we can think like it as a state exploration problem where each state represents the amount of water in both jugs at a particular point in time. From any given state, a set of possible operations can be performed to move to a new state and this continues until we either reach the desired amount d in one of the jugs or determining that it's not possible.

For this problem, the state is represented as a pair (jug1, jug2), where jug1 is the amount of water in the first jug and jug2 is the amount in the second jug. The initial state is (0, 0) because both jugs start empty. Since we're looking for the minimum number of operations, Breadth-First Search (BFS) is a good choice as it explores all possible states level by level and this make sure that the first time we find the solution, it's with the minimum number of steps.

There are six possible operations that can be applied at any given state:

  1. Fill Jug 1: Fill the first jug to its maximum capacity (m liters).
  2. Fill Jug 2: Fill the second jug to its maximum capacity (n liters).
  3. Empty Jug 1: Completely empty the first jug.
  4. Empty Jug 2: Completely empty the second jug.
  5. Pour Jug 1 into Jug 2: Pour as much water as possible from the first jug into the second jug until the second jug is full or the first jug is empty.
  6. Pour Jug 2 into Jug 1: Pour as much water as possible from the second jug into the first jug until the first jug is full or the second jug is empty.

Below is the implementation of the above approach:

C++
#include <bits/stdc++.h>
using namespace std;

// Function to find the minimum operations to obtain d litres
// in one jug
int minSteps(int m, int n, int d)
{
    // Check if the target is achievable
    if (d > max(m, n)) return -1; 
     
    // Queue for BFS: each state is (jug1, jug2, steps)
    queue<vector<int>> q;

    // For tracking the visited states
    vector<vector<bool>> visited(m + 1, 
                                 vector<bool>(n + 1, false));

    // Start with both jugs empty
    q.push({0, 0, 0});  // (jug1, jug2, steps)
    visited[0][0] = true;

    while (!q.empty()){
      
        auto curr = q.front();
        q.pop();

        int jug1 = curr[0];
        int jug2 = curr[1];
        int steps = curr[2];

        // If we have found the solution
        if (jug1 == d || jug2 == d) return steps;

        // All Possible operations are:

        // 1: Fill jug1
        if (!visited[m][jug2]){
            visited[m][jug2] = true;
            q.push({m, jug2, steps + 1});
        }

        // 2: Fill jug2
        if (!visited[jug1][n]){
            visited[jug1][n] = true;
            q.push({jug1, n, steps + 1});
        }

        // 3: Empty jug1
        if (!visited[0][jug2]){
            visited[0][jug2] = true;
            q.push({0, jug2, steps + 1});
        }

        // 4: Empty jug2
        if (!visited[jug1][0]){
            visited[jug1][0] = true;
            q.push({jug1, 0, steps + 1});
        }

        // 5: Pour jug1 into jug2
        int pour1to2 = min(jug1, n - jug2);
        if (!visited[jug1 - pour1to2][jug2 + pour1to2]){
            visited[jug1 - pour1to2][jug2 + pour1to2] = true;
            q.push({jug1 - pour1to2, jug2 + pour1to2,
                                                steps + 1});
        }

        // 6: Pour jug2 into jug1
        int pour2to1 = min(jug2, m - jug1);
        if (!visited[jug1 + pour2to1][jug2 - pour2to1]){
            visited[jug1 + pour2to1][jug2 - pour2to1] = true;
            q.push({jug1 + pour2to1, jug2 - pour2to1,
                                                steps + 1});
        }
    }

    // If no solution is found
    return -1;
}

int main(){ 
  
    // jug1 = 4 litre, jug2 = 3 litre 
    int m = 4, n = 3, d = 2;
    cout << minSteps(m, n, d);
    return 0;
}
C
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

// Function to find the minimum operations to obtain
// d liters in one jug
int minSteps(int m, int n, int d) {
  
    // Check if the target is achievable
    if (d > (m > n ? m : n)) return -1; 
	
  	//  Queue for BFS: each state is (jug1, jug2, steps)
    int q[10000][3], front = 0, rear = 0;

    // For tracking the visited states, initialized to false
    bool visited[m + 1][n + 1];
    for (int i = 0; i <= m; i++) {
        for (int j = 0; j <= n; j++) {
            visited[i][j] = false;
        }
    }

    // Start with both jugs empty, pushing the initial
    // state (0, 0, 0) 
    q[rear][0] = 0;
    q[rear][1] = 0;
    q[rear][2] = 0;
    rear++;
    visited[0][0] = true;

    while (front != rear) {
      
        // Extract the front of the queue
        int jug1 = q[front][0];
        int jug2 = q[front][1];
        int steps = q[front][2];
        front++;

        if (jug1 == d || jug2 == d) return steps;

        // 1: Fill jug1 to its maximum capacity
        if (!visited[m][jug2]) {
            visited[m][jug2] = true;
            q[rear][0] = m;
            q[rear][1] = jug2;
            q[rear][2] = steps + 1;
            rear++;
        }

        // 2: Fill jug2 to its maximum capacity
        if (!visited[jug1][n]) {
            visited[jug1][n] = true;
            q[rear][0] = jug1;
            q[rear][1] = n;
            q[rear][2] = steps + 1;
            rear++;
        }

        // 3: Empty jug1
        if (!visited[0][jug2]) {
            visited[0][jug2] = true;
            q[rear][0] = 0;
            q[rear][1] = jug2;
            q[rear][2] = steps + 1;
            rear++;
        }

        // 4: Empty jug2
        if (!visited[jug1][0]) {
            visited[jug1][0] = true;
            q[rear][0] = jug1;
            q[rear][1] = 0;
            q[rear][2] = steps + 1;
            rear++;
        }

        // 5: Pour jug1 into jug2
        int pour1to2 = jug1 < (n - jug2) ? jug1 : (n - jug2);
        if (!visited[jug1 - pour1to2][jug2 + pour1to2]) {
            visited[jug1 - pour1to2][jug2 + pour1to2] = true;
            q[rear][0] = jug1 - pour1to2;
            q[rear][1] = jug2 + pour1to2;
            q[rear][2] = steps + 1;
            rear++;
        }

        // 6: Pour jug2 into jug1
        int pour2to1 = jug2 < (m - jug1) ? jug2 : (m - jug1);
        if (!visited[jug1 + pour2to1][jug2 - pour2to1]) {
            visited[jug1 + pour2to1][jug2 - pour2to1] = true;
            q[rear][0] = jug1 + pour2to1;
            q[rear][1] = jug2 - pour2to1;
            q[rear][2] = steps + 1;
            rear++;
        }
    }

    return -1;
}

int main() {
  
    // jug1 = 4 litre, jug2 = 3 litre 
    int m = 4, n = 3, d = 2;
    int result = minSteps(m, n, d);
    printf("%d\n", result);
    return 0;
}
Java
import java.util.*;

class GfG {
  
	// Function to find the minimum operations to obtain
    // d liters in one jug
    static int minSteps(int m, int n, int d) {
        if (d > Math.max(m, n)) return -1; 

        // Queue for BFS: each state is (jug1, jug2, steps)
        Queue<int[]> q = new LinkedList<>();
      
        // Tracking visited states
        boolean[][] visited = new boolean[m + 1][n + 1]; 

        // Start with both jugs empty
        q.add(new int[] {0, 0, 0});
        visited[0][0] = true;

        while (!q.isEmpty()) {
            int[] curr = q.poll();
            int jug1 = curr[0], jug2 = curr[1], steps = curr[2];

            if (jug1 == d || jug2 == d) return steps;

            // All possible operations:

            // 1: Fill jug1
            if (!visited[m][jug2]) {
                visited[m][jug2] = true;
                q.add(new int[] {m, jug2, steps + 1});
            }

            // 2: Fill jug2
            if (!visited[jug1][n]) {
                visited[jug1][n] = true;
                q.add(new int[] {jug1, n, steps + 1});
            }

            // 3: Empty jug1
            if (!visited[0][jug2]) {
                visited[0][jug2] = true;
                q.add(new int[] {0, jug2, steps + 1});
            }

            // 4: Empty jug2
            if (!visited[jug1][0]) {
                visited[jug1][0] = true;
                q.add(new int[] {jug1, 0, steps + 1});
            }

            // 5: Pour jug1 into jug2
            int pour1to2 = Math.min(jug1, n - jug2);
            if (!visited[jug1 - pour1to2][jug2 + pour1to2]) {
                visited[jug1 - pour1to2][jug2 + pour1to2] = true;
                q.add(new int[] {jug1 - pour1to2, jug2 
                                       + pour1to2, steps + 1});
            }

            // 6: Pour jug2 into jug1
            int pour2to1 = Math.min(jug2, m - jug1);
            if (!visited[jug1 + pour2to1][jug2 - pour2to1]) {
                visited[jug1 + pour2to1][jug2 - pour2to1] = true;
                q.add(new int[] {jug1 + pour2to1, jug2 
                                        - pour2to1, steps + 1});
            }
        }

        return -1;
    }
  
    public static void main(String[] args) {
      
        // jug1 = 4 litre, jug2 = 3 litre 
        int m = 4, n = 3, d = 2;
        System.out.println(minSteps(m, n, d));
    }
}
Python
from collections import deque

# Function to find the minimum operations to obtain
# d liters in one jug
def min_steps(m, n, d):
    if d > max(m, n):
        return -1 

    # Queue for BFS: (jug1, jug2, steps)
    q = deque([(0, 0, 0)])
    
    # For tracking the visited states
    visited = [[False] * (n + 1) for _ in range(m + 1)]  
    visited[0][0] = True

    while q:
        jug1, jug2, steps = q.popleft()

        if jug1 == d or jug2 == d:
            return steps

        # 1: Fill jug1
        if not visited[m][jug2]:
            visited[m][jug2] = True
            q.append((m, jug2, steps + 1))

        # 2: Fill jug2
        if not visited[jug1][n]:
            visited[jug1][n] = True
            q.append((jug1, n, steps + 1))

        # 3: Empty jug1
        if not visited[0][jug2]:
            visited[0][jug2] = True
            q.append((0, jug2, steps + 1))

        # 4: Empty jug2
        if not visited[jug1][0]:
            visited[jug1][0] = True
            q.append((jug1, 0, steps + 1))

        # 5: Pour jug1 into jug2
        pour1to2 = min(jug1, n - jug2)
        if not visited[jug1 - pour1to2][jug2 + pour1to2]:
            visited[jug1 - pour1to2][jug2 + pour1to2] = True
            q.append((jug1 - pour1to2, jug2 + pour1to2, steps + 1))

        # 6: Pour jug2 into jug1
        pour2to1 = min(jug2, m - jug1)
        if not visited[jug1 + pour2to1][jug2 - pour2to1]:
            visited[jug1 + pour2to1][jug2 - pour2to1] = True
            q.append((jug1 + pour2to1, jug2 - pour2to1, steps + 1))

    return -1  

if __name__ == "__main__":
  
    # jug1 = 4 litre, jug2 = 3 litre 
    m, n, d = 4, 3, 2
    print(min_steps(m, n, d))
C#
using System;
using System.Collections.Generic;

// Function to find the minium mumber of step
class GfG {
  
    // Function to find the minimum operations to obtain
    // d liters in one jug
    static int MinSteps(int m, int n, int d) {
        if (d > Math.Max(m, n)) return -1; 
		
        // Queue for BFS: each state is (jug1, jug2, steps)
        Queue<int[]> q = new Queue<int[]>();
      
        // Tracking visited states
        bool[,] visited = new bool[m + 1, n + 1]; 

        // Start with both jugs empty
        q.Enqueue(new int[] {0, 0, 0});
        visited[0, 0] = true;

        while (q.Count > 0) {
            int[] curr = q.Dequeue();
            int jug1 = curr[0], jug2 = curr[1], steps = curr[2];

            if (jug1 == d || jug2 == d) return steps;

            // 1: Fill jug1
            if (!visited[m, jug2]) {
                visited[m, jug2] = true;
                q.Enqueue(new int[] {m, jug2, steps + 1});
            }

            // 2: Fill jug2
            if (!visited[jug1, n]) {
                visited[jug1, n] = true;
                q.Enqueue(new int[] {jug1, n, steps + 1});
            }

            // 3: Empty jug1
            if (!visited[0, jug2]) {
                visited[0, jug2] = true;
                q.Enqueue(new int[] {0, jug2, steps + 1});
            }

            // 4: Empty jug2
            if (!visited[jug1, 0]) {
                visited[jug1, 0] = true;
                q.Enqueue(new int[] {jug1, 0, steps + 1});
            }

            // 5: Pour jug1 into jug2
            int pour1to2 = Math.Min(jug1, n - jug2);
            if (!visited[jug1 - pour1to2, jug2 + pour1to2]) {
                visited[jug1 - pour1to2, jug2 + pour1to2] = true;
                q.Enqueue(new int[] {jug1 - pour1to2, jug2
                                           + pour1to2, steps + 1});
            }

            // 6: Pour jug2 into jug1
            int pour2to1 = Math.Min(jug2, m - jug1);
            if (!visited[jug1 + pour2to1, jug2 - pour2to1]) {
                visited[jug1 + pour2to1, jug2 - pour2to1] = true;
                q.Enqueue(new int[] {jug1 + pour2to1, jug2 
                                           - pour2to1, steps + 1});
            }
        }

        return -1;
    }

    static void Main(string[] args) {
      
        // jug1 = 4 litre, jug2 = 3 litre 
        int m = 4, n = 3, d = 2;
        Console.WriteLine(MinSteps(m, n, d));
    }
}
JavaScript
function minSteps(m, n, d) {
    if (d > Math.max(m, n)) return -1;
	
    // Queue for BFS: each state is (jug1, jug2, steps)
    let queue = [[0, 0, 0]];
    
    // Tracking visited states
    let visited = Array.from({ length: m + 1 }, 
                            () => Array(n + 1).fill(false));
    visited[0][0] = true;

    while (queue.length > 0) {
        let [jug1, jug2, steps] = queue.shift();

        if (jug1 === d || jug2 === d) return steps;

        // 1: Fill jug1
        if (!visited[m][jug2]) {
            visited[m][jug2] = true;
            queue.push([m, jug2, steps + 1]);
        }

        // 2: Fill jug2
        if (!visited[jug1][n]) {
            visited[jug1][n] = true;
            queue.push([jug1, n, steps + 1]);
        }

        // 3: Empty jug1
        if (!visited[0][jug2]) {
            visited[0][jug2] = true;
            queue.push([0, jug2, steps + 1]);
        }

        // 4: Empty jug2
        if (!visited[jug1][0]) {
            visited[jug1][0] = true;
            queue.push([jug1, 0, steps + 1]);
        }

        // 5: Pour jug1 into jug2
        let pour1to2 = Math.min(jug1, n - jug2);
        if (!visited[jug1 - pour1to2][jug2 + pour1to2]) {
            visited[jug1 - pour1to2][jug2 + pour1to2] = true;
            queue.push([jug1 - pour1to2, jug2 + pour1to2,
                                                 steps + 1]);
        }

        // 6: Pour jug2 into jug1
        let pour2to1 = Math.min(jug2, m - jug1);
        if (!visited[jug1 + pour2to1][jug2 - pour2to1]) {
            visited[jug1 + pour2to1][jug2 - pour2to1] = true;
            queue.push([jug1 + pour2to1, jug2 - pour2to1,
                                                steps + 1]);
        }
    }

    return -1;
}

// jug1 = 4 litre, jug2 = 3 litre 
let m = 4, n = 3, d = 2;
console.log(minSteps(m, n, d));

Output
4

Time Complexity: O(n*m), Where n and m are the quantity of jug1 and jug2, respectively.
Auxiliary Space: O(n*m).


Next Article
Practice Tags :

Similar Reads