【Leetcode】365. Water and Jug Problem(配数学证明)

题目地址:

https://leetcode.com/problems/water-and-jug-problem/

给两个容积为 x x x y y y的水瓶,允许以下操作:
1、将一个瓶灌满水;
2、将一个瓶清空;
3、从一个瓶向另一个瓶灌水,直到第一个瓶空或者第二个瓶满为止。
问是否存在凑出 z z z水量的方案。凑出的意思是,要么其中某个瓶里有 z z z的水量,要么两个瓶子水量之和是 z z z(这两句话其实都可以归结为后一句话,因为如果有一个瓶凑出了 z z z,那就将另一个瓶子清空就好了)。

首先如果 x + y < z x+y<z x+y<z,那显然不可能。在 z ≤ x + y z\le x+y zx+y的情况下做考虑。可以用数学归纳法来证明,任意时刻的各自的水量一定是 m x + n y mx+ny mx+ny这种类型的整数(因此总水量也是)。所以最后凑出 z z z的一个必要条件就是存在 m m m n n n使得 m x + n y = z mx+ny=z mx+ny=z由Bezout定理知道,这等价于 gcd ⁡ ( x , y ) ∣ z \gcd(x,y)|z gcd(x,y)z(当然上面的 x + y ≥ z x+y\ge z x+yz的必要条件也不能少)。

接下来证明充分性,即如果存在 m m m n n n使得 m x + n y = z mx+ny=z mx+ny=z,并且 x + y ≥ z x+y\ge z x+yz,那么一定能把 z z z水量凑出来。可以用数学归纳法。显然如果 m n ≥ 0 mn\ge 0 mn0结论成立。不妨设 m > 0 m>0 m>0 n < 0 n<0 n<0,令 n ′ = − n n'=-n n=n。假设对所有 m + n ′ ≤ k m+n'\le k m+nk都成立,我们证明对 m + n ′ = k + 1 m+n'=k+1 m+n=k+1的情形也成立。那么这个情形要么是多加一次 x x x,要么是多减一次 y y y。如果 ( m + 1 ) x + n y ≤ x + y (m+1)x+ny\le x+y (m+1)x+nyx+y,则有 m x + n y ≤ y mx+ny\le y mx+nyy,所以当凑出 m x + n y mx+ny mx+ny的时候,就将其全倒入 y y y中,再将 x x x灌满即可凑出 ( m + 1 ) x + n y (m+1)x+ny (m+1)x+ny。如果是 m x + ( n − 1 ) y ≥ 0 mx+(n-1)y\ge 0 mx+(n1)y0,那么有 m x + n y ≥ y mx+ny\ge y mx+nyy,所以当凑出 m x + n y mx+ny mx+ny的时候,就将其全倒入 y y y中直到 y y y满,再将 y y y倒出去即可凑出 m x + ( n − 1 ) y mx+(n-1)y mx+(n1)y。而 m = n = 0 m=n=0 m=n=0的情形显然可以凑出,由数学归纳法,所有形如 m x + n y mx+ny mx+ny且小于等于 x + y x+y x+y z z z都能被凑出来。结论成立。

至于求最大公约数,可以用辗转相除法。不妨设 x ≥ y x\ge y xy,做带余除法 x = q 1 y + r 1 x=q_1y+r_1 x=q1y+r1,再做带余除法 y = q 2 r 1 + r 2 y=q_2r_1+r_2 y=q2r1+r2,这样一直做下去,由于 r 1 > r 2 > . . . r_1>r_2>... r1>r2>...,所以一定会递降到 0 0 0,递降到 0 0 0的时候的那个 q i q_i qi就是最大公约数。代码如下:

public class Solution {
    public boolean canMeasureWater(int x, int y, int z) {
    	// 特判
        if (x + y < z) {
            return false;
        }
        
        // 把y换成较小数,x换成较大数
        if (y > x) {
            int tmp = x;
            x = y;
            y = tmp;
        }
        
        // 辗转相除法求最大公约数
        while (y != 0) {
            int tmp = y;
            y = x % y;
            x = tmp;
        }
        
        return z % x == 0;
    }
}

时间复杂度 O ( log ⁡ min ⁡ { x , y } ) O(\log \min\{x,y\}) O(logmin{x,y}),空间 O ( 1 ) O(1) O(1)

时间复杂度的证明可以这样想,我们考虑每次相除的的被除数,这个数最多经过两次相除就要减半。原因是,考虑 x / y x/y x/y,并且 x > y x>y x>y,如果 y < n 2 y<\frac{n}{2} y<2n,那么显然经过一次相除 x x x就要被赋值为 y y y,从而减半;如果 y ≥ n 2 y\ge \frac{n}{2} y2n,那么显然经过两次相除 x x x就要被赋值为余数,而余数是小于 y y y的,从而也会减半。所以时间复杂度就等于问 x x x y y y的较大数能减半多少次。其实不用问较大数,因为只要经过一次相除,较大数就变为较小数了,所以可以写成时间复杂度是 O ( log ⁡ min ⁡ { x , y } ) O(\log \min\{x,y\}) O(logmin{x,y})

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值