LeetCode 365. 水壶问题

本文为打卡刷Leetcode题系列文章, 这个系列文章的目录都是按照如下四个部分构成

  • 题目链接
  • 题目描述
  • 代码初步
    这部分写的是我在刷题过程中的思路,相信在拿到题目就立马参考大神们的思路写代码是不会有进步的,我思故我在,思考让我进步!!
  • 代码欣赏
    这一部分po出优秀的解题答案,这里我们可以学习大神们的解题思路,进而内化成自己的
    ———————————————————————————————————

题目链接

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

题目描述

在这里插入图片描述

代码初步

直觉告诉我这里面有数学规律,但是我是个菜鸡,我找不到:<

代码欣赏

  • 方法1:数学解法:
    找到x,y的最大公约数,并判断Z是否是它的倍数。

    根据题目,我们的每次操作只会让桶里的水总量增加x,增加y,减少x,减少y.
    反证法:假设往不满的桶里放水,或者把它排空呢?我们分成这两种情况讨论。

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

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

    以上,我们可以认为每次操作只会给水的总量带来x或者y的变化量,因此我们的目标可以改写成:找到一对整数a,b使得: ax + by = z

    • 若a<0,那么可以进行以下操作:
      往 y 壶倒水;把 y 壶的水倒入 x 壶;如果 y 壶不为空,那么 x 壶肯定是满的,把 x 壶倒空,然后再把 y 壶的水倒入 x 壶。

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

    1. 若b<0,方法同上,x 与 y 互换
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) 项

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值