题目描述
有两个容量分别为 x升 和 y升 的水壶以及无限多的水。请判断能否通过使用这两个水壶,从而可以得到恰好 z升 的水?
如果可以,最后请用以上水壶中的一或两个来盛放取得的 z升 水。
你允许:
装满任意一个水壶
清空任意一个水壶
从一个水壶向另外一个水壶倒水,直到装满或者倒空
示例 1: (From the famous “Die Hard” example)
输入: x = 3, y = 5, z = 4
输出: True
示例 2:
输入: x = 2, y = 6, z = 5
输出: False
解法一:数学(贝祖定理/裴蜀定理/最大公约数)
-
每次操作只会让桶里的水
- 增加x,减少x,增加y,减少y
-
同时注意以下情况
- 两个桶不可能同时有水且不满(即,至少有一个为空或满)
- 对不满的桶加水没意义 (相当于初始状态加满水)
- 不满的水倒掉没意义 (回到初始状态 另外一个水壶空/满)
-
转换为数学公式:
- 对于x,y操作,找到一对整数 a,b使得
- ax+by=z
-
满足z<=x+y且存在a,b 即可达成目标( z不能超过两个满水壶之和)
- a>=0,b>=0 显然达成目标
- a<0
- 往y倒水
- 把y壶的水倒入x
- 把x的水倒空,y的水倒入x。
- 重复以上,即是重复 增加b次y,减少a次x 最后的x壶得到的就是z
-
贝祖定理告诉我们 ax+by=z有解当且仅当x,y的最大公约数的倍数,即找出x,y的最大公约数,判断值的倍数是否为z的。
class Solution {
public boolean canMeasureWater(int x, int y, int z) {
if(z==0) return true;
if(x==0&&y==0||z>x+y) return false;
int value=gcd(x,y);
return z%value==0;
}
public int gcd(int a,int b){
if(b>a) return gcd(b,a);
return b==0?a:gcd(b,a%b);
}
}
解法二:广度优先搜索(极简)
- (5,0)->(2,3)->(2,0)->(0,2)->(5,2)->(4,3) 找到z=4
- 反过来从(0,3)开始也可以找到
- 该代码效率不一定高,但比较整洁
class Solution {
public boolean canMeasureWater(int x, int y, int z) {
if(z<0||x+y<z){
return false;
}
Queue<Integer> queue =new LinkedList();
Set<Integer> set=new HashSet<>();
queue.add(0);
while(!queue.isEmpty()){
int i=queue.poll();
if(i+x<=x+y&&set.add(i+x)){
queue.add(i+x);
}
if(i+y<=x+y&&set.add(i+y)){
queue.add(i+y);
}
if(i-x>=0&&set.add(i-x)){
queue.add(i-x);
}
if(i-y>=0&&set.add(i-y)){
queue.add(i-y);
}
if(set.contains(z)){
return true;
}
}
return false;
}
}