裴蜀定理之水壶问题
面试被问到水壶问题,发现leetcode上有原题,涉及到裴蜀定理
有两个容量分别为 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
思路一
因为每次水壶有三种操作,加满,倒出,相互倒,所以我们可以暴力枚举所有状态 ,但是我试了一下好像会超限
思路二
运用裴蜀定理:
在数论中,裴蜀定理是一个关于最大公约数(或最大公约式)的定理。裴蜀定理得名于法国数学家艾蒂安·裴蜀,说明了对任何整数a、b和它们的最大公约数d,关于未知数x和y的线性丢番图方程(称为裴蜀等式): ax + by = m 有解当且仅当m是d的倍数。裴蜀等式有解时必然有无穷多个整数解,每组解x、y都称为裴蜀数,可用辗转相除法求得。 例如,12和42的最大公因子是6,则方程12x + 42y = 6有解。事实上有(-3)×12 + 1×42 = 6及4×12 + (-1)×42 = 6。 特别来说,方程 ax + by = 1 有解当且仅当整数a和b互素。
证明(看的我懵懵懂懂)不过大概知道如何应用就ok
对于(a,b∈Z)(a,b∈Z),ax+by=gcd(a,b)ax+by=gcd(a,b)一定有整数解x,yx,y的证明: 设d=gcd(a,b)d=gcd(a,b),可得d∣ad∣a,d∣bd∣b,且d∣(ax+by)d∣(ax+by)。 设ss是aa与bb的线性组合集中最小的正元素,并且对于某个x,y∈Zx,y∈Z,有s=ax+bys=ax+by,可知s∈Zs∈Z。 设q=⌊a/s⌋q=⌊a/s⌋,则有r=amods=a−qs=a−q(ax+by)=a(1−qx)+b(−qy)r=amods=a−qs=a−q(ax+by)=a(1−qx)+b(−qy),因此rr也是aa与bb的一个线性组合,已知ss是这个线性集合中的最小正整数,又0≤r<s0≤r<s,可得r=0r=0,因此有s∣as∣a,同理有s∣bs∣b,因此ss是aa与bb的公约数,所以有d≥sd≥s。因为对于任意x,y∈Zx,y∈Z,有d∣(ax+by)d∣(ax+by),而对于某个x,y∈Zx,y∈Z,有s=ax+bys=ax+by,所以有d∣sd∣s。但d∣sd∣s且s>0s>0,可得d≤sd≤s。综合d≤sd≤s和d≥sd≥s,得d=sd=s,故ss=gcd(a,b)gcd(a,b)。我们已知ss是aa与bb的线性组合集中的最小正元素,故ax+by=gcd(a,b)ax+by=gcd(a,b)一定有整数解x,yx,y,亦可知对于a,b∈Za,b∈Z,gcd(a,b)gcd(a,b)是ax+by(x,y∈Z)ax+by(x,y∈Z)的最小正元素
对于(a,b∈Z)(a,b∈Z),ax+by=cax+by=c有整数解的条件是gcd(a,b)∣cgcd(a,b)∣c的证明: 充分性:设d=gcd(a,b)d=gcd(a,b),已知ax+by=dax+by=d一定有整数解,设其解为(x0,y0)(x0,y0)。d∣cd∣c,则存在k∈Zk∈Z,使得c=kd=k(ax+by)=a(kx)+b(ky)c=kd=k(ax+by)=a(kx)+b(ky),即解为(kx0,ky0)(kx0,ky0)。 必要性:因ax1+by1=c,x1,y1∈Zax1+by1=c,x1,y1∈Z,设d=gcd(a,b)d=gcd(a,b),有d∣ad∣a,d∣bd∣b,d∣(ax1,by1)d∣(ax1,by1),即d∣c
为什么可以运用裴署定理解决这道题(转自leetcode的高赞题解)
转换证明 完备的证明需要包含两部分:
证明任意一轮游戏结果可以表示成 ax + byax+by 。 证明任意 ax + byax+by 可以表示成一轮游戏结果 名词说明:
倒水:把某桶的水倒光。 补水:把某桶的水补满。 匀水:一桶往另一桶倒水。 任意一轮游戏结果可以表示成 ax + by 推论一:不可能两个桶同时有水且不满。
观察所有的操作,所有的结束条件都为某桶满、或者某桶空。
推论二:补水操作只会对空桶进行。
证明方法一
首先补水没必要对满桶进行。 假设对一个不满且非空的桶进行补水操作,另一个桶只可能是空桶或满桶(推论一)。 最终两个桶都为满桶或者空桶之一,这通看起来很厉害的操作并无卵用。 证明方法二
假设对一个非空桶进行了补水操作。 可以把这个操作拆成两个操作,先倒,再补,最终结果一致。 推论三:倒水操作只会对满桶进行。
证明方法一
首先倒水没必要对空桶进行。 假设对一个不满且非空的桶进行倒水操作,另一个桶只可能是空桶或满桶(推论一)。 最终两个桶都为满桶或者空桶之一,这通看起来也很厉害的也没什么卵用。 证明方法二
假设对一个非空桶进行了倒水操作。 可以把这个操作拆成两个操作,先补,再倒,最终结果一致。 得证:任意一轮游戏结果可以表示成 ax + by
推论二说明了补水操作导致总量增加 x 或 y 。 推论三说明了倒水操作导致总量减少 x 或 y 。 任意 ax + by可以表示成一轮游戏结果
public boolean canMeasureWater(int x, int y, int z) {
if(x == 0 && y == 0) return z == 0;
return z == 0 || (z % gcd(x,y) == 0 && x + y >= z);
}
static int gcd(int x,int y){
if(y == 0) return x;
int r = x % y;
return gcd(y,r);
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。