原题网址:https://leetcode.com/problems/water-and-jug-problem/
You are given two jugs with capacities x and y litres. There is an infinite amount of water supply available. You need to determine whether it is possible to measure exactly zlitres using these two jugs.
Operations allowed:
- Fill any of the jugs completely.
- Empty any of the jugs.
- Pour water from one jug into another till the other jug is completely full or the first jug itself is empty.
Example 1: (From the famous "Die Hard" example)
Input: x = 3, y = 5, z = 4 Output: True
Example 2:
Input: x = 2, y = 6, z = 5 Output: False
方法一:递归+记忆化搜索(但结果会栈溢出)。
public class Solution {
private boolean[][] visit;
private boolean find(int a, int b, int x, int y, int z) {
if (visit[a][b]) return false;
visit[a][b] = true;
if (a + b == z) return true;
// Fill in jug a
if (a < x && find(x, b, x, y, z)) return true;
// Fill in jug b
if (b < y && find(a, y, x, y, z)) return true;
// Empty jug a
if (a > 0 && find(0, b, x, y, z)) return true;
// Empty jug b
if (b > 0 && find(a, 0, x, y, z)) return true;
// Pour from a to b
if (a > 0 && b < y) {
int pour = Math.min(a, y - b);
if (find(a - pour, b + pour, x, y, z)) return true;
}
// Pour from b to a
if (b > 0 && a < x) {
int pour = Math.min(b, x - a);
if (find(a + pour, b - pour, x, y, z)) return true;
}
return false;
}
public boolean canMeasureWater(int x, int y, int z) {
visit = new boolean[x + 1][y + 1];
return find(0, 0, x, y, z);
}
}
方法二:广度优先搜索(但结果会超时)。
public class Solution {
public boolean canMeasureWater(int x, int y, int z) {
if (x + y < z) return false;
Set<Long> visit = new HashSet<>();
visit.add(0L);
while (true) {
Set<Long> found = new HashSet<>();
for(long state : visit) {
int a = (int)(state >>> 32);
int b = (int)(state & 0b11111111111111111111111111111111);
// System.out.printf("a=%d, b=%d, z=%d\n", a, b, z);
if (a + b == z) return true;
// Fill in jug a
if (a < x) {
long next = getKey(x, b);
if (!visit.contains(next)) found.add(next);
}
// Fill in jug b
if (b < y) {
long next = getKey(a, y);
if (!visit.contains(next)) found.add(next);
}
// Empty jug a
if (a > 0) {
long next = getKey(0, b);
if (!visit.contains(next)) found.add(next);
}
// Empty jug b
if (b > 0) {
long next = getKey(a, 0);
if (!visit.contains(next)) found.add(next);
}
// Pour from a to b
if (a > 0 && b < y) {
int pour = Math.min(a, y - b);
long next = getKey(a - pour, b + pour);
if (!visit.contains(next)) found.add(next);
}
// Pour from b to a
if (b > 0 && a < x) {
int pour = Math.min(b, x - a);
long next = getKey(a + pour, b - pour);
if (!visit.contains(next)) found.add(next);
}
}
if (found.isEmpty()) return false;
visit.addAll(found);
}
}
private long getKey(int a, int b) {
return (((long)a) << 32) | b;
}
}
方法三:运用数学原理(不懂,参考LeetCode论坛)。
public class Solution {
private int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
public boolean canMeasureWater(int x, int y, int z) {
return z == 0 || (z <= x + y && z % gcd(x, y) == 0);
}
}