有两个容量分别为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
解题思路
这个问题首先不叫容易想到暴力解,也就是dfs
。如果定义函数
f
(
a
,
b
,
c
)
f(a,b,c)
f(a,b,c)表示三个壶中包含a
、b
、c
升水对应的结果。那么,可以分成三个类别考虑
- 装满任意一个水壶
- f ( x , b ) ∣ ∣ f ( a , y ) f(x,b)||f(a,y) f(x,b)∣∣f(a,y)
- 清空任意一个水壶
- f ( 0 , b ) ∣ ∣ f ( a , 0 ) f(0,b)||f(a,0) f(0,b)∣∣f(a,0)
- 从一个水壶向另外一个水壶倒水,直到装满或者倒空
a
中的水向b
中倒, f ( 0 , a + b ) 当 a + b < y f(0,a+b)当a+b<y f(0,a+b)当a+b<y或者 f ( a + b − y , y ) 当 a + b > = y f(a+b-y,y)当a+b>=y f(a+b−y,y)当a+b>=yb
中的水向a
中倒, f ( a + b , 0 ) 当 a + b < x f(a+b,0)当a+b<x f(a+b,0)当a+b<x或者 f ( x , a + b − x ) 当 a + b > = x f(x,a+b-x)当a+b>=x f(x,a+b−x)当a+b>=x
最后边界条件,就是当a=z
或b=z
或a+b=z
的时候可以解。
from functools import lru_cache
class Solution:
def canMeasureWater(self, x: int, y: int, z: int) -> bool:
if x + y < z: return False
@lru_cache(None)
def dfs(a, b):
if a == z or b == z or a + b == z: return True
res = False
res |= dfs(x, b) | dfs(a, y) | dfs(0, b) | dfs(a, 0)
if a + b < y: res |= dfs(0, a + b)
else: res |= dfs(a + b - y, y)
if a + b < x: res |= dfs(a + b, 0)
else: res |= dfs(x, a + b - x)
return res
return dfs(0, 0)
dfs
迭代过深了,尝试bfs
来写。
class Solution:
def canMeasureWater(self, x: int, y: int, z: int) -> bool:
if x + y < z: return False
q = [(0, 0)]
vis = set([(0, 0)])
while q:
a, b = q.pop(0)
if a == z or b == z or a + b == z: return True
states = [(x, b), (a, y), (0, b), (a, 0)]
if a + b < y: states.append((0, a + b))
else: states.append((a + b - y, y))
if a + b < x: states.append((a + b, 0))
else: states.append((x, a + b - x))
for state in states:
if state in vis: continue
vis.add(state)
q.append(state)
return False
这个问题更好的解法是通过数学,假设a
个x
和b
个y
能得到z
- a x + b y = z ax+by=z ax+by=z
想要上述方程成立的话,需要满足z%gcd(x,y)==0
,这就是贝祖定理。
同样需要处理边界问题,当x+y<z
的时候直接返回False
。如果x=0
或者y=0
,此时需要判断z
是否为0
或者x+y
是否等于z
。
class Solution:
def canMeasureWater(self, x: int, y: int, z: int) -> bool:
if x + y < z:
return False
if x == 0 or y == 0:
return z == 0 or x + y == z
return z % math.gcd(x, y) == 0
需要注意贝祖定理得到的解是整数解,也就是说a
和b
存在负数的情况,如何理解?可以理解为对应的水壶倒空多少次。
我将该问题的其他语言版本添加到了我的GitHub Leetcode
如有问题,希望大家指出!!!