/** Node in Pouring Water Problem * @author Lin Chen * @since 11.13.2017 */ public class Node { private int a = 10, b = 7, c = 4;//size of containers private int A, B, C;//real water volume in container private Node prev;//predecessor public Node(int A, int B, int C) { this.A = A; this.B = B; this.C = C; prev = null; } public int getA() {return A;} public int getB() {return B;} public int getC() {return C;} public int geta() {return a;} public int getb() {return b;} public int getc() {return c;} public Node getPrev() {return prev;} public void setA(int A) {this.A = A;} public void setB(int B) {this.B = B;} public void setC(int C) {this.C = C;} public void setPrev(Node n) {prev = n;} @Override public boolean equals(Object n) { Node temp = (Node)n; if((A == temp.A) && (B == temp.B) && (C == temp.C)) return true; return false; } @Override public int hashCode() { return A+B*10+C*100; } public String toString() { return "("+A+"/"+a+","+B+"/"+c+","+C+"/"+c+")"; } }
/** Program to demo pouring water * @author Lin Chen * @since 11.13.2017 */ import java.util.*; import javafx.util.*; public class P { /** Calculate the remaining water after pouring water from A to B * @param A volume of water in container A * @param B volume of water in container B * @param a volume of container A * @param b volume of container B * @return (volume in A, volume in B) */ public static Pair<Integer, Integer> pouring(int A, int B, int a, int b) { int tempA, tempB; if((b-B) > A) { tempA = 0; tempB = B+A; } else { tempB = b; tempA = A - (b-B); } return new Pair<Integer, Integer>(tempA, tempB); } /** Pouring water operation * @param A volume of water in A * @param B volume of water in B * @param C volume of water in C * @param A volume of container A * @param B volume of container B * @param C volume of container C * @param direction pouring direction * @return {@code Node}, a node from the operation */ public static Node pouringWater(int A, int B, int C, int a, int b, int c, String direction) { if(direction.equals("AB")) { Pair<Integer, Integer> p = pouring(A, B, a, b); return new Node(p.getKey(), p.getValue(), C); } else if(direction.equals("AC")) { Pair<Integer, Integer> p = pouring(A, C, a, c); return new Node(p.getKey(), B, p.getValue()); } else if(direction.equals("BC")) { Pair<Integer, Integer> p = pouring(B, C, b, c); return new Node(A, p.getKey(), p.getValue()); } else if(direction.equals("BA")) { Pair<Integer, Integer> p = pouring(B, A, b, a); return new Node(p.getValue(), p.getKey(), C); } else if(direction.equals("CA")) { Pair<Integer, Integer> p = pouring(C, A, c, a); return new Node(p.getValue(), B, p.getKey()); } else if(direction.equals("CB")) { Pair<Integer, Integer> p = pouring(C, B, c, b); return new Node(A, p.getValue(), p.getKey()); } else return null; } /** Check if the pouring from A to B is a valid operation * @param A volume of water in A * @param B volume of water in B * @param a volume of container A * @param b volume of container B * @return {@code true}, valide; {@code false}, otherwise */ public static boolean isValid(int A, int B, int a, int b) { if(A <= 0) return false; if(B >= b) return false; return true; } /** Search all children of the current node, if they are valid and not visited, push into queue * @param q a queue of Node * @param u current node * @param m hash table to check if the node has been visited */ public static void addChildren(Queue<Node> q, Node u, HashMap<Node, Integer> m) { int A, B, C, a, b, c; A = u.getA(); B = u.getB(); C = u.getC(); a = u.geta(); b = u.getb(); c = u.getc(); Node v; if(isValid(A, B, a, b)) { v = pouringWater(A, B, C, a, b, c, "AB"); v.setPrev(u); if(!m.containsKey(v)) { q.add(v); m.put(v, 0); } } if(isValid(A, C, a, c)) { v = pouringWater(A, B, C, a, b, c, "AC"); v.setPrev(u); if(!m.containsKey(v)) { q.add(v); m.put(v, 0); } } if(isValid(B, A, b, a)) { v = pouringWater(A, B, C, a, b, c, "BA"); v.setPrev(u); if(!m.containsKey(v)) { q.add(v); m.put(v, 0); } } if(isValid(B, C, b, c)) { v = pouringWater(A, B, C, a, b, c, "BC"); v.setPrev(u); if(!m.containsKey(v)) { q.add(v); m.put(v, 0); } } if(isValid(C, A, c, a)) { v = pouringWater(A, B, C, a, b, c, "CA"); v.setPrev(u); if(!m.containsKey(v)) { q.add(v); m.put(v, 0); } } if(isValid(C, B, c, b)) { v = pouringWater(A, B, C, a, b, c, "CB"); v.setPrev(u); if(!m.containsKey(v)) { q.add(v); m.put(v, 0); } } } /** Check if the current node is the target node * @param u node * @param target target volume * @return {@code true}, target node; {@code false}, otherwise */ public static boolean checkNode(Node u, int target) { if(u.getA() == target) return true; if(u.getB() == target) return true; if(u.getC() == target) return true; return false; } /** Breadth First Search all possible nodes * @param s start node * @param target target volume of water * @return target node */ public static Node BFS(Node s, int target) { Queue<Node> q = new LinkedList<Node>(); HashMap<Node, Integer> m = new HashMap<Node, Integer>(); q.add(s); m.put(s, 0); while(!q.isEmpty()) { Node u = q.poll(); if(checkNode(u, target)) return u; addChildren(q, u, m); } return null; } /** print path finding the target node * @param u target node */ public static void printNode(Node u) { if(u == null) return; printNode(u.getPrev()); System.out.println(u); } public static void main(String args[]) { Node s = new Node(0, 7, 4); Node t = BFS(s, 2); printNode(t); } }