LeetCode 365.水壶问题
有明确的初始状态和最终状态
可以使用BFS来解决,主要考虑下一步可能状态next的求解,是个体力活
import java.util.*;
class Solution {
private int x;
private int y;
private HashSet<String> status = new HashSet<>();
public boolean canMeasureWater(int x, int y, int z) {
this.x = x;
this.y = y;
if(z == 0) return true;
Queue<String> que = new LinkedList<>();
que.add(x + " " + y);
while (!que.isEmpty()) {
String poll = que.remove();
for (String next : next(poll)) {
que.add(next);
int num1, num2;
String[] s = poll.split(" ");
num1 = Integer.parseInt(s[0]);
num2 = Integer.parseInt(s[1]);
if(num1 == z || num2 == z || (num1 + num2 == z)){
return true;
}
}
}
return false;
}
private HashSet<String> next(String poll) {
HashSet<String> nexts = new HashSet<>();
int num1, num2;
String[] s = poll.split(" ");
num1 = Integer.parseInt(s[0]);
num2 = Integer.parseInt(s[1]);
//装满
if (num1 < x && !status.contains(x + " " + num2)) {
nexts.add(x + " " + num2);
status.add(x + " " + num2);
}
if (num2 < y && !status.contains(num1 + " " + y)) {
nexts.add(num1 + " " + y);
status.add(num1 + " " + y);
}
//清空
if (num1 > 0 && !status.contains(0 + " " + num2)) {
status.add(x + " " + num2);
nexts.add(x + " " + num2);
}
if (num2 > 0 && !status.contains(num1 + " " + 0)) {
nexts.add(num1 + " " + 0);
status.add(num1 + " " + 0);
}
//倒
if (num1 > 0 && num2 < y) {
int left = y - num2;
if(num1 > left && !status.contains((num1 - left)+" "+ y)) {
nexts.add((num1 - left) + " " + y);
status.add((num1 - left) + " " + y);
}
if(num1 == left && !status.contains(0+" "+y)) {
nexts.add(0 + " " + y);
status.add(0 + " " + y);
}
if(num1 < left && !status.contains(0+" "+(num1+num2))) {
nexts.add(0 + " " + (num1 + num2));
status.add(0 + " " + (num1 + num2));
}
}
if (num2 > 0 && num1 < x) {
int left = x - num1;
if(num2 > left && !status.contains(x+" "+ (num2 - left))) {
nexts.add(x + " " + (num2 - left));
status.add(x + " " + (num2 - left));
}
if(num2 == left && !status.contains(x+" "+0)) {
nexts.add(x + " " + 0);
status.add(x+" "+0);
}
if(num2 < left && !status.contains((num1+num2)+" "+0)) {
nexts.add((num1 + num2) + " " + 0);
status.add((num1+num2)+" "+0);
}
}
return nexts;
}
}
解法二:贝祖定理
我们认为,每次操作只会让桶里的水总量增加 x
,增加 y
,减少 x
,或者减少 y
。
你可能认为这有问题:如果往一个不满的桶里放水,或者把它排空呢?那变化量不就不是 x
或者 y
了吗?接下来我们来解释这一点:
首先要清楚,在题目所给的操作下,两个桶不可能同时有水且不满。因为观察所有题目中的操作,操作的结果都至少有一个桶是空的或者满的;
其次,对一个不满的桶加水是没有意义的。因为如果另一个桶是空的,那么这个操作的结果等价于直接从初始状态给这个桶加满水;而如果另一个桶是满的,那么这个操作的结果等价于从初始状态分别给两个桶加满;
再次,把一个不满的桶里面的水倒掉是没有意义的。因为如果另一个桶是空的,那么这个操作的结果等价于回到初始状态;而如果另一个桶是满的,那么这个操作的结果等价于从初始状态直接给另一个桶倒满。
因此,我们可以认为每次操作只会给水的总量带来 x 或者 y 的变化量。因此我们的目标可以改写成:找到一对整数 a
, b
,使得
ax+by=z
而只要满足 z≤x+y
,且这样的 a
,b
存在,那么我们的目标就是可以达成的。这是因为:
若 a≥0
,b≥0
,那么显然可以达成目标。
若 a<0
,那么可以进行以下操作:
往 y
壶倒水;
把 y
壶的水倒入 x
壶;
如果 y
壶不为空,那么 x
壶肯定是满的,把 x
壶倒空,然后再把 y
壶的水倒入 x
壶。
重复以上操作直至某一步时 x
壶进行了 a
次倒空操作,y 壶进行了 b
次倒水操作。
若 b<0
,方法同上,x
与 y
互换。
而贝祖定理告诉我们,ax+by=z
有解当且仅当 z
是 x
,y
的最大公约数的倍数。因此我们只需要找到 x,
y
的最大公约数并判断 z
是否是它的倍数即可。
class Solution {
boolean canMeasureWater(int x, int y, int z) {
if (x + y < z) return false;
if (x == 0 || y == 0) return z == 0 || x + y == z;
return z % gcd(x, y) == 0;
}
private int gcd(int x, int y) {
if(y%x == 0)
return x;
return gcd(y%x,x);
}
}