zoukankan      html  css  js  c++  java
  • 【LeetCode】365. 水壶问题

    题目链接:

    365. 水壶问题

    题目描述:

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

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

    你允许:

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

    示例:

    示例 1:

    输入:x = 3, y = 5, z = 4
    输出:true
    

    示例 2:

    输入:x = 2, y = 6, z = 5
    输出:false
    

    思路:

    先放一个预备知识:

    裴蜀定理(又称贝祖定理):维基百科百度百科

    简单来讲就是:

    若 x, y 是整数,且它们的最大公约数为 d,那么对于任意的整数 a, b,ax+by 都一定是 d 的倍数。特别地,一定存在整数 a,b,使 ax+by=d 成立。

    值得注意的是:a,b 可以是负数。

    下面的内容是官方题解,我觉得十分精彩,就搬过来了。原链接:官方题解

    另外,题目条件十分值得认真审阅:

    1. 最后请用以上水壶中的一或两个来盛放取得的 z 升水:那么如果 x + y < z,直接 false
    2. 根据题目给出的三个倒水的条件,可以得到这样的结论:每次操作只会让桶里的水总量增加 x 或增加 y或减少 x或者减少 y

    你可能认为这有问题:如果往一个不满的桶里放水,或者把它排空呢?那变化量不就不是 x 或者 y 了吗?接下来解释这一点:

    • 首先要清楚,在题目所给的操作下,两个桶不可能同时有水且不满。因为观察所有题目中的操作,操作的结果都至少有一个桶是空的或者满的;

    • 其次,对一个不满的桶加水是没有意义的。因为如果另一个桶是空的,那么这个操作的结果等价于直接从初始状态给这个桶加满水;而如果另一个桶是满的,那么这个操作的结果等价于从初始状态分别给两个桶加满;

    • 再次,把一个不满的桶里面的水倒掉是没有意义的。因为如果另一个桶是空的,那么这个操作的结果等价于回到初始状态;而如果另一个桶是满的,那么这个操作的结果等价于从初始状态直接给另一个桶倒满。

    因此,可以认为每次操作只会给水的总量带来 x 或者 y 的变化量。因此目标可以改写成:

    找到一对整数 a, b,使得 (ax+by=z)

    而只要满足 (zleq x+y),且这样的 a, b 存在,那么我们的目标就是可以达成的。这是因为:

    • (ageq 0, bgeq 0),那么显然可以达成目标。

    • (alt 0),那么可以进行以下操作:

      1. 往 y 壶倒水;
      2. 把 y 壶的水倒入 x 壶;
      3. 如果 y 壶不为空,那么 x 壶肯定是满的,把 x 壶倒空,然后再把 y 壶的水倒入 x 壶。

      重复以上操作直至某一步时 x 壶进行了 a 次倒空操作,y 壶进行了 b 次倒水操作。

    • (blt 0),方法同上,x 与 y 互换。

    而贝祖定理告诉我们,(ax+by=z) 有解当且仅当 z 是 x, y 的最大公约数的倍数。因此

    只需要找到 x, y 的最大公约数并判断 z 是否是它的倍数即可

    另附:辗转相除法求最大公约数

    辗转相除法是递归算法,一句话概括这个算法就是:两个整数的最大公约数,等于其中较小的数 和两数相除余数 的最大公约数
    比如 10 和 25,25 除以 10 商 2 余 5,那么 10 和 25 的最大公约数,等同于 10 和 5 的最大公约数。

    代码实现:

    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 % gcd(x, y) == 0;
        }
        // 辗转相除法求最大公约数
        private int gcd(int a, int b) {
            return (a % b == 0) ? b : gcd(b, a % b);
        }
    }
    
  • 相关阅读:
    C#中的Json序列化
    c#在sqlserver中使用EF框架
    Mvc中模拟模型
    localdb启动
    List泛型用法(半转载半原创)
    C#中真正的属性
    委托的简介、使用和简单事件
    类嵌套_list泛型_餐馆点菜例
    JavaIO
    JavaIO
  • 原文地址:https://www.cnblogs.com/ME-WE/p/12537442.html
Copyright © 2011-2022 走看看