水壶问题

leetcode 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

 

刚开始的思路想直接但模拟水壶这个过程看可能的取值,后面绝的是在模拟不了啦。看了以下官方的解答。

 

第一种解法:利用深度优先遍历搜索所有可能满足的情况, 每次总共右六种情况, X倒满, X倒控, Y倒满, Y倒空, X倒到Y, Y倒到X

  由于情况实在太多,我们需要记录每次计算过的数据,防止重复计算。实现的时候出现一个问题,重写equals后,没有重写hashCode方法导致重复计算问题。

static class Point {
        int remainX;
        int remainY;

        public Point(int remainX, int remainY) {
            this.remainX = remainX;
            this.remainY = remainY;
        }

        @Override
        public int hashCode() {
            return Integer.hashCode(remainX)+Integer.hashCode(remainY);
        }

        @Override
        public boolean equals(Object obj) {
            Point point = (Point) obj;
            return point.remainX == this.remainX && point.remainY == this.remainY;
        }
    }

/**
     * 深度优先遍历
     * @param x
     * @param y
     * @param z
     * @return
     */
    public  boolean canMeasureWater2(int x, int y, int z) {
        Stack<Point> stack = new Stack<>();
        stack.push(new Point(0, 0));
        HashSet<Point> allVisitedDatas = new HashSet<>();
        while (!stack.isEmpty()) {
            Point point = stack.pop();
            int remainX = point.remainX;
            int remainY = point.remainY;

            if (remainX + remainY == z) return true;

            if (allVisitedDatas.contains(point)) continue;
            else allVisitedDatas.add(point);

            // X倒满
            stack.push(new Point(x, remainY));
            // X倒空
            stack.push(new Point(0, remainY));
            // Y倒满
            stack.push(new Point(remainX, y));
            // Y倒空
            stack.push(new Point(remainX, 0));
            // X倒到Y
            stack.push(new Point(remainX - Math.min(remainX, y - remainY), remainY + Math.min(remainX, y - remainY)));
            // Y倒到X
            stack.push(new Point(remainX + Math.min(x - remainX, remainY), remainY - Math.min(remainY, x - remainX)));
        }
        return false;
    }

 

第二种方法使用贝祖定理, 本质上倒水问题可以看作ax+by=z, 此问题存在解的条件是z是x和y最大公约数的倍数。当然,我们还需要知道最终最多只能有两个水壶装水,同时最终至少有一个水壶为空或者满。

 // 贝祖定理 ax+by=z 存在z是a,b的最大公约数的倍数
    public boolean canMesureWater1(int x, int y, int z){
         // 首先判断约束条件
if (x + y < z) return false; if (x == 0 || y == 0) { return z == 0 || x + y == z; } int tmpt = -1; if (y > x) { // 始终保持x>=y tmpt = x; x = y; y = tmpt; } while ((tmpt = x % y) != 0) { x = y; y = tmpt; } return z % y == 0; }

 

posted @ 2020-03-26 15:14  张秀杰  阅读(534)  评论(0编辑  收藏  举报