Home

365. Water and Jug Problem

Detailed explanation coming soon!

/*
 * Multimap to keep track of what coordinates we have seen to prevent looping
 * Depth first search, there are 6 actions possible at each (x,y)
 *   - fill x up to max
 *   - fill y up to max
 *   - transfer x to y (if applicable)
 *   - transfer y to x (if applicable)
 *   - drop x
 *   - drop y
 *
 * no multimap in java, so lets use a map of first index -> hashset // or override hashCode
 *
 *
 * OR: just use Bezout's identity
 * */

// Bezout's identity version:

/*
public boolean canMeasureWater(int x, int y, int z) {
  //limit brought by the statement that water is finallly in one or both buckets
  if(x + y < z) return false;
  //case x or y is zero
  if( x == z || y == z || x + y == z ) return true;

  //get GCD, then we can use the property of Bézout's identity
  return z%GCD(x, y) == 0;
}

public int GCD(int a, int b){
  while(b != 0 ){
    int temp = b;
    b = a%b;
    a = temp;
  }
  return a;
}
*/


public class Solution {
  private static class Pair {
    public int x;
    public int y;

    public Pair(int x, int y) {
      this.x = x;
      this.y = y;
    }
  }

  private static class HashPairSet {
    private HashMap<Integer, HashSet<Integer>> map;

    public HashPairSet() {
      map = new HashMap<Integer, HashSet<Integer>>();
    }

    public void insert(Pair pair) {
      HashSet<Integer> ySet = map.get(pair.x);
      
      if (ySet == null) {
        ySet = new HashSet<Integer>();
        map.put(pair.x, ySet);
      }

      ySet.add(pair.y);
    }

    public boolean contains(Pair pair) {
      HashSet<Integer> ySet = map.get(pair.x);

      if (ySet == null) return false;

      return ySet.contains(pair.y);
    }
  }

  // Modifies stack and set if pair is unique, otherwise do nothing
  public static void addPossibility(Pair pair, ArrayDeque<Pair> stack, HashPairSet set) {
    if (!set.contains(pair)) {
      stack.addFirst(pair);
      set.insert(pair);
    }
  }

  public boolean canMeasureWater(int xMax, int yMax, int target) {
    HashPairSet set = new HashPairSet();

    ArrayDeque<Pair> stack = new ArrayDeque<Pair>();
    stack.addFirst(new Pair(0, 0));

    while(!stack.isEmpty()) {
      Pair pair = stack.removeFirst();

      if (pair.x == target ||
          pair.y == target ||
          (pair.x + pair.y) == target) {
        return true;
      }

      // Fill 'x' up to max
      if (pair.x != xMax) {
        addPossibility(new Pair(xMax, pair.y), stack, set);
      }

      // Fill 'y' up to max
      if (pair.y != yMax) {
        addPossibility(new Pair(pair.x, yMax), stack, set);
      }

      // Transfer 'x' to 'y'
      if (yMax != pair.y) {
        int transfer = Math.min(yMax - pair.y, pair.x);
        addPossibility(new Pair(pair.x - transfer, pair.y + transfer), stack, set);
      }

      // Transfer 'y' to 'x'
      if (xMax != pair.x) {
        int transfer = Math.min(pair.y, xMax - pair.x);
        addPossibility(new Pair(pair.x + transfer, pair.y - transfer), stack, set);
      }

      // Drop 'x'
      if (pair.x != 0) {
        addPossibility(new Pair(0, pair.y), stack, set);
      }

      // Drop 'y'
      if (pair.y != 0) {
        addPossibility(new Pair(pair.x, 0), stack, set);
      }
    }

    return false;
  }
}


Questions? Have a neat solution? Comment below!