【数论 or BFS】LeetCode 365. 水壶问题

虽然现在保研成功了,我也深知了算法的重要性,还是每天刷一道吧,防止老年痴呆 = =

一、题目大意

有两个容量分别为 x升 和 y升 的水壶以及无限多的水。请判断能否通过使用这两个水壶,从而可以得到恰好 z升 的水?

如果可以,最后请用以上水壶中的一或两个来盛放取得的 z升 水。

你允许:

  • 装满任意一个水壶
  • 清空任意一个水壶
  • 从一个水壶向另外一个水壶倒水,直到装满或者倒空

来源:力扣(LeetCode)
链接:原题链接

PS:不好意思,之前说很讨厌LeetCode,到以工作为目的的刷题了,还是来了,真香 ~

二、题目思路以及AC代码

这道题有两个思路进行求解,其实准确的说是三个,下面我就一一来说。下面均假设B水壶容量y > A水壶容量x

解法1:

首先,显然该题可以用BFS进行解决,状态也显而易见,就是两个水壶的状态,暂且称其为A,B水壶,初始状态为(0, 0),因为两个水壶都是空的,然后在处于每个状态的时候,可以进行题目中提到的三种操作,产生新的状态,对应的也就是新的状态进栈,之后就是经典的BFS了。

解法2:

同样还是用BFS解决,但比解法1稍微快捷方便一点。解法1中,我们将两个水壶的状态共同作为BFS的状态,但是注意题目要求的是求得两个水壶的总水量是否可以达到z,那么我们这时就可以将总水量作为状态进行BFS求解。

初始状态为0,因为总水量是0,然后对于每个状态来说,总水量都可以进行+x,+y,-x,-y的操作(当然要保证在0 ~ x+y的范围内),当然要记住这里不限制必须操作只一次,因为有的状态变化可能不能一次达成。

解法3:

这里就没有采用BFS求解了,因为由解法2我们知道,两个水壶的总水量必定可以表示成 ax + by 的形式,其中 a 和 b 均为整数。

因为我们从一开始,每回加水,必定加x或者y,有人可能会有疑问,如果B水壶先装满水,倒到A水壶里面,那么B水壶里面就剩一点,再倒不就不是全部的y了吗?但是此时确实全部的x,因为总水量是x + y,而你在加水之前是 y。每回倒水,倒水之后,必定剩下 x 或 y。每回两个壶相互倒水,水的总量必定不会变。

当我们知道了两个水壶总量必定是 ax + by 之后,就需要知道一个定理。

贝祖定理:若x,y是整数,且gcd(x,y)=d,那么对于任意的整数a,b,ax+by都一定是d的倍数,特别地,一定存在整数a,b,使ax+by=d成立。

所以思路也很明确,我们只需要求 x 和 y 的最大公约数,然后看 z 是否是其的倍数即可。

因为我在做题的时候就只写了第二种解法,下面就先贴这种解法的代码吧,之后有时间再把另外两种补上:

// 解法 2
#include <queue>
#include <unordered_set>
class Solution {
public:
    bool canMeasureWater(int x, int y, int z) {
        if (z == 0) return true;
        if (z > (x + y)) return false;

        unordered_set<int> vis;
        queue<int> q;
        q.push(0);
        vis.insert(0);

        while (!q.empty()) {
            int n = q.front(); q.pop();

            if (n + x <= x + y && vis.find(n+x) == vis.end()) {
                if (n+x == z) return true;
                vis.insert(n+x);
                q.push(n+x);
            }
            if (n + y <= x + y && vis.find(n+y) == vis.end()) {
                if (n+y == z) return true;
                vis.insert(n+y);
                q.push(n+y);
            }
            if (n - x >= 0 && vis.find(n-x) == vis.end()) {
                if (n-x == z) return true;
                vis.insert(n-x);
                q.push(n-x);
            }
            if (n - y >= 0 && vis.find(n-y) == vis.end()) {
                if (n-y == z) return true;
                vis.insert(n-y);
                q.push(n-y);
            }
        }

        return false;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值