Water Jug Problem using Memoization
Given two jugs with the maximum capacity of m and n liters respectively. The jugs don’t have markings on them which can help us to measure smaller quantities. The task is to measure d liters of water using these two jugs. Hence our goal is to reach from initial state (m, n) to final state (0, d) or (d, 0). Examples:
Input: 4 3 2 Output: (0, 0) –> (4, 0) –> (4, 3) –> (0, 3) –> (3, 0) –> (3, 3) –> (4, 2) –> (0, 2) Input: 5 2 4 Output: (0, 0) –> (5, 0) –> (5, 2) –> (0, 2) –> (2, 0) –> (2, 2) –> (4, 0)
Approach: An approach using BFS has been discussed in the previous post. In this post an approach using memoization and recursion has been discussed. At any point, there can be a total of six possibilities:
- Empty the first jug completely
- Empty the second jug completely
- Fill the first jug
- Fill the second jug
- Fill the water from the second jug into the first jug until the first jug is full or the second jug has no water left
- Fill the water from the first jug into the second jug until the second jug is full or the first jug has no water left
Approach: Using Recursion, visit all the six possible moves one by one until one of them returns True. Since there can be repetitions of same recursive calls, hence every return value is stored using memoization to avoid calling the recursive function again and returning the stored value. Below is the implementation of the above approach:
C++
#include <bits/stdc++.h>
using namespace std;
int jug1 = 4;
int jug2 = 3;
int aim = 2;
int visited[10000][10000]={ false };
bool waterJugSolver( int amt1, int amt2){
if ((amt1 == aim && amt2 == 0) || (amt2 == aim && amt1 == 0)){
cout<<amt1<< " " <<amt2<<endl;
return true ;
}
if (visited[amt1][amt2] == false ){
cout<<amt1<< " " <<amt2<<endl;
visited[amt1][amt2] = true ;
return (waterJugSolver(0, amt2) ||
waterJugSolver(amt1, 0) ||
waterJugSolver(jug1, amt2) ||
waterJugSolver(amt1, jug2) ||
waterJugSolver(amt1 + min(amt2, (jug1-amt1)),
amt2 - min(amt2, (jug1-amt1))) ||
waterJugSolver(amt1 - min(amt1, (jug2-amt2)),
amt2 + min(amt1, (jug2-amt2))));
}
else
return false ;
}
int main() {
cout<<( "Steps: " )<<endl;
waterJugSolver(0, 0);
return 0;
}
|
Java
import java.util.HashSet;
public class Main {
static int jug1 = 4 ;
static int jug2 = 3 ;
static int aim = 2 ;
static HashSet<String> visited = new HashSet<>();
static boolean waterJugSolver( int amt1, int amt2)
{
if ((amt1 == aim && amt2 == 0 )
|| (amt2 == aim && amt1 == 0 )) {
System.out.println(amt1 + " " + amt2);
return true ;
}
String state = amt1 + " " + amt2;
if (!visited.contains(state)) {
System.out.println(state);
visited.add(state);
return (
waterJugSolver( 0 , amt2)
|| waterJugSolver(amt1, 0 )
|| waterJugSolver(jug1, amt2)
|| waterJugSolver(amt1, jug2)
|| waterJugSolver(
amt1 + Math.min(amt2, (jug1 - amt1)),
amt2 - Math.min(amt2, (jug1 - amt1)))
|| waterJugSolver(
amt1 - Math.min(amt1, (jug2 - amt2)),
amt2 + Math.min(amt1, (jug2 - amt2))));
}
else {
return false ;
}
}
public static void main(String[] args)
{
System.out.println( "Steps: " );
waterJugSolver( 0 , 0 );
}
}
|
Python3
from collections import defaultdict
jug1, jug2, aim = 4 , 3 , 2
visited = defaultdict( lambda : False )
def waterJugSolver(amt1, amt2):
if (amt1 = = aim and amt2 = = 0 ) or (amt2 = = aim and amt1 = = 0 ):
print (amt1, amt2)
return True
if visited[(amt1, amt2)] = = False :
print (amt1, amt2)
visited[(amt1, amt2)] = True
return (waterJugSolver( 0 , amt2) or
waterJugSolver(amt1, 0 ) or
waterJugSolver(jug1, amt2) or
waterJugSolver(amt1, jug2) or
waterJugSolver(amt1 + min (amt2, (jug1 - amt1)),
amt2 - min (amt2, (jug1 - amt1))) or
waterJugSolver(amt1 - min (amt1, (jug2 - amt2)),
amt2 + min (amt1, (jug2 - amt2))))
else :
return False
print ("Steps: ")
waterJugSolver( 0 , 0 )
|
C#
using System;
using System.Collections.Generic;
public class MainClass
{
static int jug1 = 4;
static int jug2 = 3;
static int aim = 2;
static HashSet< string > visited = new HashSet< string >();
static bool waterJugSolver( int amt1, int amt2)
{
if ((amt1 == aim && amt2 == 0)
|| (amt2 == aim && amt1 == 0)) {
Console.WriteLine(amt1 + " " + amt2);
return true ;
}
string state = amt1 + " " + amt2;
if (!visited.Contains(state)) {
Console.WriteLine(state);
visited.Add(state);
return (
waterJugSolver(0, amt2)
|| waterJugSolver(amt1, 0)
|| waterJugSolver(jug1, amt2)
|| waterJugSolver(amt1, jug2)
|| waterJugSolver(
amt1 + Math.Min(amt2, (jug1 - amt1)),
amt2 - Math.Min(amt2, (jug1 - amt1)))
|| waterJugSolver(
amt1 - Math.Min(amt1, (jug2 - amt2)),
amt2 + Math.Min(amt1, (jug2 - amt2))));
}
else {
return false ;
}
}
public static void Main( string [] args)
{
Console.WriteLine( "Steps: " );
waterJugSolver(0, 0);
}
}
|
Javascript
let jug1 = 4;
let jug2 = 3;
let aim = 2;
let visited = new Set();
function waterJugSolver(amt1, amt2) {
if ((amt1 == aim && amt2 == 0) || (amt2 == aim && amt1 == 0)) {
console.log(amt1, amt2);
return true ;
}
let key = `${amt1}-${amt2}`;
if (!visited.has(key)) {
console.log(amt1, amt2);
visited.add(key);
return (
waterJugSolver(0, amt2) ||
waterJugSolver(amt1, 0) ||
waterJugSolver(jug1, amt2) ||
waterJugSolver(amt1, jug2) ||
waterJugSolver(
amt1 + Math.min(amt2, jug1 - amt1),
amt2 - Math.min(amt2, jug1 - amt1)
) ||
waterJugSolver(
amt1 - Math.min(amt1, jug2 - amt2),
amt2 + Math.min(amt1, jug2 - amt2)
)
);
} else {
return false ;
}
}
console.log( "Steps:" );
waterJugSolver(0, 0);
|
Output:
Steps:
0 0
4 0
4 3
0 3
3 0
3 3
4 2
0 2
Time complexity: O(M * N) Auxiliary Space: O(M * N)
Last Updated :
21 Mar, 2023
Like Article
Save Article
Share your thoughts in the comments
Please Login to comment...