Leetcode 365.水壶问题

在这里插入图片描述

有明确的初始状态和最终状态

可以使用BFS来解决,主要考虑下一步可能状态next的求解,是个体力活

import java.util.*;

class Solution {
    private int x;
    private int y;
    private HashSet<String> status = new HashSet<>();
    public boolean canMeasureWater(int x, int y, int z) {
        this.x = x;
        this.y = y;
        if(z == 0) return true;
        Queue<String> que = new LinkedList<>();
        que.add(x + " " + y);
        while (!que.isEmpty()) {
            String poll = que.remove();
            for (String next : next(poll)) {
                que.add(next);
                int num1, num2;
                String[] s = poll.split(" ");
                num1 = Integer.parseInt(s[0]);
                num2 = Integer.parseInt(s[1]);
                if(num1 ==  z || num2 == z || (num1 + num2 == z)){
                    return true;
                }
            }

        }
        return false;
    }

    private HashSet<String> next(String poll) {
        HashSet<String> nexts = new HashSet<>();
        int num1, num2;
        String[] s = poll.split(" ");
        num1 = Integer.parseInt(s[0]);
        num2 = Integer.parseInt(s[1]);
        //装满
        if (num1 < x && !status.contains(x + " " + num2)) {
            nexts.add(x + " " + num2);
            status.add(x + " " + num2);
        }
        if (num2 < y && !status.contains(num1 + " " + y)) {
            nexts.add(num1 + " " + y);
            status.add(num1 + " " + y);
        }
        //清空
        if (num1 > 0 && !status.contains(0 + " " + num2)) {
            status.add(x + " " + num2);
            nexts.add(x + " " + num2);
        }
        if (num2 > 0 && !status.contains(num1 + " " + 0)) {
            nexts.add(num1 + " " + 0);
            status.add(num1 + " " + 0);
        }
        //倒
        if (num1 > 0 && num2 < y) {
            int left = y - num2;
            if(num1 > left && !status.contains((num1 - left)+" "+ y)) {
                nexts.add((num1 - left) + " " + y);
                status.add((num1 - left) + " " + y);
            }
            if(num1 == left && !status.contains(0+" "+y)) {
                nexts.add(0 + " " + y);
                status.add(0 + " " + y);
            }
            if(num1 < left && !status.contains(0+" "+(num1+num2))) {
                nexts.add(0 + " " + (num1 + num2));
                status.add(0 + " " + (num1 + num2));
            }
        }
        if (num2 > 0 && num1 < x) {
            int left = x - num1;
            if(num2 > left && !status.contains(x+" "+ (num2 - left))) {
                nexts.add(x + " " + (num2 - left));
                status.add(x + " " + (num2 - left));
            }
            if(num2 == left && !status.contains(x+" "+0)) {
                nexts.add(x + " " + 0);
                status.add(x+" "+0);
            }
            if(num2 < left && !status.contains((num1+num2)+" "+0)) {
                nexts.add((num1 + num2) + " " + 0);
                status.add((num1+num2)+" "+0);
            }
        }
        return nexts;
    }
}

解法二:贝祖定理

我们认为,每次操作只会让桶里的水总量增加 x,增加 y,减少 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,方法同上,xy 互换。

而贝祖定理告诉我们,ax+by=z有解当且仅当 zx,y 的最大公约数的倍数。因此我们只需要找到 x, y 的最大公约数并判断 z 是否是它的倍数即可。

class Solution {
    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 % gcd(x, y) == 0;
    }

    private int gcd(int x, int y) {
        if(y%x == 0)
            return x;
        return gcd(y%x,x);
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值