365、水壶问题
有两个容量分别为 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
题解
1、深度优先搜索
在任意一个时刻,我们可以且仅可以采取以下几种操作:
- 把 X 壶的水灌进 Y 壶,直至灌满或倒空;
- 把 Y 壶的水灌进 X 壶,直至灌满或倒空;
- 把 X 壶灌满;
- 把 Y 壶灌满;
- 把 X 壶倒空;
- 把 Y 壶倒空
import java.util.*;
class Solution {
public boolean canMeasureWater(int x, int y, int z) {
if (z == 0) {
return true;
}
if (x + y < z) {
return false;
}
Queue<Map.Entry<Integer, Integer>> queue = new ArrayDeque<>();
AbstractMap.SimpleEntry<Integer, Integer> start = new AbstractMap.SimpleEntry<>(0, 0);
queue.add(start);
Set<Map.Entry<Integer, Integer>> visited = new HashSet<>();
visited.add(start);
while (!queue.isEmpty()) {
Map.Entry<Integer, Integer> entry = queue.poll();
int curX = entry.getKey();
int curY = entry.getValue();
if (curX == z || curY == z || curX + curY == z) {
return true;
}
if (curX == 0) {
// 把第一个桶填满
addIntoQueue(queue, visited, new AbstractMap.SimpleEntry<>(x, curY));
}
if (curY == 0) {
// 把第二个桶填满
addIntoQueue(queue, visited, new AbstractMap.SimpleEntry<>(curX, y));
}
if (curY < y) {
// 把第一个桶倒空
addIntoQueue(queue, visited, new AbstractMap.SimpleEntry<>(0, curY));
}
if (curX < x) {
// 把第二个桶倒空
addIntoQueue(queue, visited, new AbstractMap.SimpleEntry<>(curX, 0));
}
// y - curY是第二个桶还可以再加的水的升数,但是最多只能加curX升水。
int moveSize = Math.min(curX, y - curY);
// 把第一个桶里的curX升水倒到第二个桶里去。
addIntoQueue(queue, visited, new AbstractMap.SimpleEntry<>(curX - moveSize, curY + moveSize));
// 反过来同理,x - curX是第一个桶还可以再加的升数,但是最多只能加curY升水。
moveSize = Math.min(curY, x - curX);
// 把第一个桶里的curX升水倒到第二个桶里去。
addIntoQueue(queue, visited, new AbstractMap.SimpleEntry<>(curX + moveSize, curY - moveSize));
}
return false;
}
private void addIntoQueue(Queue<Map.Entry<Integer, Integer>> queue,
Set<Map.Entry<Integer, Integer>> visited,
Map.Entry<Integer, Integer> newEntry) {
if (!visited.contains(newEntry)) {
visited.add(newEntry);
queue.add(newEntry);
}
}
}
2、数学原理
z 必须为x、y的最小公倍数的倍数。
详情见:https://leetcode-cn.com/problems/water-and-jug-problem/solution/shui-hu-wen-ti-by-leetcode-solution/
class Solution {
// 求a,b的最小公倍数
private int gcd(int a, int b)
{
if(b == 0)
return a;
return gcd(b, a % b);
}
public 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);
}
// 判断:z 是否为x、y的最小公倍数的倍数。
return z % gcd(x,y) == 0;
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1056615746@qq.com