leetcode *365. 水壶问题(待研究)

【题目】*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】BFS

1.两个杯子不会同时处于半满状态(若是与外部交互则倒满或者倒空一瓶,内部交互则把自己倒空或者倒满另一瓶).
2.若一个半满杯子水量改变,则只能是倒空或者由另一杯倒满(根据上面可知另一杯只能是满或者空,此时由外部倒满或者倒空没有意义,因为会变成双满或者一空一满或者双空的类初始状态)
3.综上所述,倒空或由外部倒满半满杯子没有意义.水的总量变化只能取决于倒空满杯子或者倒满空杯子.

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】数学方法:GCB最大公约数,贝祖定理

每次操作只会让桶里的水总量增加 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 是否是它的倍数即可。

扫描二维码关注公众号,回复: 10727060 查看本文章
class Solution {
    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;
        }
        return z % gcb(x,y) == 0;
    }

    int gcb(int x,int y){
        return y == 0 ? x : gcb(y,x%y);
    }
}
发布了175 篇原创文章 · 获赞 135 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/XunCiy/article/details/105366728