有两个容量分别为 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
解法一:
此题只需要判断是否能得到z升水,无需关注过程,故可使用数学知识直接解答。当z 能被x 和y 的最大公因数整除时,即能用x,y 测出z 升水。使用辗转相除法求最大公因数,再与z 取模进行判断
class solution{
public boolean canMeasureWater(int x, int y, int z) {
if (z == 0) {
return true;
}
if (x + y < z) {
return false;
} else {
return z % gcd(x, y) == 0 ? true : false;
}
}
}
解法二:
BFS,思路参考:LeetCode题解
/**
* 表示桶两桶情况的类,x,y分别表示两桶内水的体积
*/
private class MyState {
private int x;
private int y;
public int getX() {
return x;
}
public int getY() {
return y;
}
public MyState(int x, int y) {
this.x = x;
this.y = y;
}
//如果重写以下方法会提示运行超时
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MyState state = ( MyState) o;
return x == state.x &&
y == state.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
public boolean canMeasureWater(int x, int y, int z) {
// 特殊情况判定
if (z == 0) {
return true;
}
if (x + y < z) {
return false;
}
// 初始化情况
MyState initState = new MyState(0, 0);
// 使用队列进行广度优先遍历
// 使用set标记已经遍历过的情况
Queue<MyState> queue = new LinkedList<>();
Set<MyState> visited = new HashSet<>();
queue.offer(initState);
visited.add(initState);
// 广度遍历
while (!queue.isEmpty()) {
MyState head = queue.poll();
int curX = head.getX();
int curY = head.getY();
// 对获取值进行一次判断
if (curX == z || curY == z || curX + curY == z) {
return true;
}
// 获取后去下一步可能出现的状态
List<MyState> nextStates = getNextStates(curX, curY, x, y);
// 打开以便于观察,调试代码
// System.out.println(head + " => " + nextStates);
for (MyState nextState : nextStates) {
if (!visited.contains(nextState)) {
queue.offer(nextState);
// 添加到队列以后,必须马上设置为已经访问,否则会出现死循环
visited.add(nextState);
}
}
}
return false;
}
/**
* 获取所有可能状态
*
* @param curX 当前x桶的水量
* @param curY 当前y桶的水量
* @param x x桶的容积
* @param y y桶的容积
* @return 所有可能的情况
*/
private List<MyState> getNextStates(int curX, int curY, int x, int y) {
List<MyState> nextStates = new ArrayList<>(8);
// 外部加水把x桶加满
MyState nextState1 = new MyState(x, curY);
// 外部加水把y桶加满
MyState nextState2 = new MyState(curX, y);
// 把x桶清空
MyState nextState3 = new MyState(0, curY);
// 把y桶清空
MyState nextState4 = new MyState(curX, 0);
// x桶的水到给y桶
// y桶满了,x桶有剩余
MyState nextState5 = new MyState(curX - (y - curY), y);
// y桶未满,x桶倒空
MyState nextState6 = new MyState(0, curY + curX);
// y桶的水打入x桶
// x桶满了,y桶有剩余
MyState nextState7 = new MyState(x, curY - (x - curX));
// x桶未满,y桶已空
MyState nextState8 = new MyState(curX + curY, 0);
//桶没满时倒水进桶
if (curX < x) {
nextStates.add(nextState1);
}
if (curY < y) {
nextStates.add(nextState2);
}
//桶有水时,将水倒出
if (curX > 0) {
nextStates.add(nextState3);
}
if (curY > 0) {
nextStates.add(nextState4);
}
// 有剩余才倒
if (curX - (y - curY) > 0) {
nextStates.add(nextState5);
}
if (curY - (x - curX) > 0) {
nextStates.add(nextState7);
}
// 倒过去倒不满才倒
if (curX + curY < y) {
nextStates.add(nextState6);
}
if (curX + curY < x) {
nextStates.add(nextState8);
}
return nextStates;
}