本文为打卡刷Leetcode题系列文章, 这个系列文章的目录都是按照如下四个部分构成
- 题目链接
- 题目描述
- 代码初步
这部分写的是我在刷题过程中的思路,相信在拿到题目就立马参考大神们的思路写代码是不会有进步的,我思故我在,思考让我进步!! - 代码欣赏
这一部分po出优秀的解题答案,这里我们可以学习大神们的解题思路,进而内化成自己的
———————————————————————————————————
题目链接
https://leetcode-cn.com/problems/water-and-jug-problem/solution/
题目描述
代码初步
直觉告诉我这里面有数学规律,但是我是个菜鸡,我找不到:<
代码欣赏
-
方法1:数学解法:
找到x,y的最大公约数,并判断Z是否是它的倍数。根据题目,我们的每次操作只会让桶里的水总量增加x,增加y,减少x,减少y.
反证法:假设往不满的桶里放水,或者把它排空呢?我们分成这两种情况讨论。
-
给一个不满的桶里加水是没有意义的。因为如果另一个桶是空的,那么这个操作的结果等价于直接从初始状态给这个桶加满水;而如果另一个桶是满的,那么这个操作等价于从初始状态分别给两个桶加满
-
把一个不满的桶里面的水倒掉是没有意义的。因为如果另一个桶是空的,那么这个操作的结果等价于回到初始状态;而如果另一个桶是满的,那么这个操作的结果等价于从初始状态直接给另一个桶倒满水。
以上,我们可以认为每次操作只会给水的总量带来x或者y的变化量,因此我们的目标可以改写成:找到一对整数a,b使得: ax + by = z
- 若a<0,那么可以进行以下操作:
往 y 壶倒水;把 y 壶的水倒入 x 壶;如果 y 壶不为空,那么 x 壶肯定是满的,把 x 壶倒空,然后再把 y 壶的水倒入 x 壶。
重复以上操作直至某一步时 x 壶进行了 a 次倒空操作,y 壶进行了 b 次倒水操作
- 若b<0,方法同上,x 与 y 互换
- 若a<0,那么可以进行以下操作:
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
else:
return z%math.gcd(x,y) == 0
复杂度分析:
-
时间复杂度:O(\log(\min(x, y)))O(log(min(x,y))),取决于计算最大公约数所使用的辗转相除法。
-
空间复杂度:O(1)O(1),只需要常数个变量。
-
方法二:深度优先(DFS)
思路和算法:
首先对题目进行建模,该问题的状态可以由两个数字决定:X壶中的水量,以及Y壶中的水量。
在任意一个时刻,我们可以且仅可以采取以下几种操作:- 把 X 壶的水灌进 Y 壶
- 把 Y 壶的水灌进 X 壶
- 把 X 壶灌满;
- 把 Y 壶灌满;
- 把 X 壶倒空;
- 把 Y 壶倒空。
因此本题可以使用深度优先搜索来解决。搜索的第一步以remain_x,remain_y作为状态,即表示X壶和Y壶中的水量。在每一步搜索时,我们会依次尝试所有的操作,递归的搜索下去。这可能导致我们陷入无止境的递归,因此我么需要使用一个哈希结合存储已经搜索过的remain_x,remain_y,状态,保证每个状态至多只被搜索一次。
在实际的代码编写中,由于深度优先搜索导致的递归远远超过了 Python 的默认递归层数,因此下面的代码使用栈来模拟递归,避免了真正使用递归而导致的问题
class Solution:
def canMeasureWater(self, x: int, y: int, z: int) -> bool:
self.seen=set()
stack = [(0,0)]
while stack:
remain_x,remain_y = stack.pop()
if remain_x == z or remain_y==z or remain_x+remain_y==z:
return True
if (remain_x,remain_y) in self.seen:
continue
self.seen.add((remain_x, remain_y))
# 把X壶灌满
stack.append((x,remain_y))
# 把 Y 壶灌满
stack.append((remain_x,y))
# 把 X 壶倒空
stack.append((0,remain_y))
# 把 Y 壶倒空
stack.append((remain_x,0))
# 把 X 壶的水灌进 Y 壶
stack.append((remain_x - min(remain_x,y-remain_y), remain_y+min(remain_x,y-remain_y)))
# 把 Y 壶的水灌进 X 壶
stack.append((remain_x+min(remain_y, x-remain_x), remain_y-min(remain_y, x-remain_x)))
return False
复杂度分析:
时间复杂度:O(xy),状态数最多有 (x+1)(y+1)
空间复杂度:O(xy),状态数最多有 (x+1)(y+1) 种,哈希集合中最多会有 (x+1)(y+1) 项