leetcode 365.水壶问题

https://leetcode-cn.com/problems/water-and-jug-problem/![在这里插入图片描述](https://img-blog.csdnimg.cn/20200321222916984.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hobXk3Nw==,size_16,color_FFFFFF,t_70

应该是比较眼熟的题目了
只有两个水壶,分别为x和y升,经过若干次装水倒水,问能不能取出z升水
不失一般性,假设x小于y,先写几个特例看看有没有规律

  1. z大于x+y的时候一定取不出,接下来的能取出的z都应该满足z<=(x+y)
  2. z为x或y的倍数的时候一定能取出
  3. z为y-x的时候一定能取出,进一步推断,z为y-x的倍数时候一定能取出,预测一下,当y-x=1的时候能取出所有满足1的z
  4. 考虑到有可能y-x会大于x,例如7-3=4,这种情况需要继续讨论,对于x=3,y=7,z=5的情况,5是可以取得到的,但是它并不在上述的三个条件之中,说明我们漏掉了一些条件。继续猜测,当z为gcd(x,y)的倍数的时候,应该也能取得到

在这里插入图片描述
这个样例醉了。。好吧题目确实没说x和y的范围

class Solution:
    def canMeasureWater(self, x: int, y: int, z: int) -> bool:
        if z>x+y:
            return False
        if x==0 or y==0:
            return True if z==x or z==y else False
        d=abs(x-y)
        if z%x==0 or z%y==0 or z%d==0 or z%math.gcd(x,y)==0:
            return True
        return False

在这里插入图片描述
通过了

题解

这道题的题解很强大啊。。
官方题解: https://leetcode-cn.com/problems/water-and-jug-problem/solution/shui-hu-wen-ti-by-leetcode-solution/
有DFS的,有BFS的,还有数学解法
这道题到底是怎么转成搜索的我是真的不懂,太弱了

在这里插入图片描述
我之前很奇怪为什么可以深搜,看了题解有点明白了,就是模拟上面的六种操作,然后枚举两个水壶的状态,直到某个状态达到题目的要求
水壶的状态有点像这样,我们发现每个状态只需要递归一次就行了
图片来自:https://www.youtube.com/watch?v=0Oef3MHYEC0

不看题解尝试自己写一下BFS
于是一个异常丑陋的BFS就诞生了

class Solution:
    def canMeasureWater(self, x: int, y: int, z: int) -> bool:
        if z>x+y:
            return False
        if x==0 or y==0:
            return True if z==x or z==y else False
        st=list()   #递归调用栈
        record=set()    #存储水壶状态,入栈时记录
        st.append((x,0))  #初始状态,第一个元素标识第一个水壶
        record.add((x,0))
        while st:
            status=list(st.pop())   #取出栈顶
            if sum(status) == z:
                return True
            if (status[0],0) not in record:     #倒掉y中的水
                record.add((status[0],0))
                st.append((status[0],0))
            if (0,status[1]) not in record:     #倒掉x中的水
                record.add((0,status[1]))
                st.append((0,status[1]))
            if (x,status[1]) not in record:     #装满x
                record.add((x,status[1]))
                st.append((x,status[1]))
            if (status[0],y) not in record:     #装满y
                record.add((status[0],y))
                st.append((status[0],y))
            #标识x,y水壶到满还需要多少升
            numx,numy=x-status[0],y-status[1]
            #x往y里面倒水,直到x空或者y满
            x2y=(0,status[0]+status[1]) if numy>status[0] else (status[0]-numy,y)
            #y往x里面倒水,直到x满或者y空
            y2x=(status[0]+status[1],0) if numx>status[1] else (x,status[1]-numx)
            if x2y not in record:
                record.add(x2y)
                st.append(x2y)
            if y2x not in record:
                record.add(y2x)
                st.append(y2x)
        return False

执行用时也是十分的夸张啊 😅…
在这里插入图片描述
看看人家题解这个BFS,不用写那么多个if,直接append,只使用set来判断是否压过栈,那么上面的6个if都可以精简成一个了
并且最后两个状态写得也很棒啊,用min来

class Solution:
    def canMeasureWater(self, x: int, y: int, z: int) -> bool:
        stack = [(0, 0)]
        self.seen = set()
        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

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/water-and-jug-problem/solution/shui-hu-wen-ti-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

照着上面简化了一下我的BFS,但是速度还不是很高的样子。。
在这里插入图片描述
其实写出DFS改成BFS就很简单了,只需要把栈改成队列就行了
下面来看一下数学解法
在这里插入图片描述
第一句话是可以理解的,看上面那张状态图就知道了
在这里插入图片描述
emmm…总感觉之前上密码学的时候学过啊。。
贴一篇证明的博文:https://blog.csdn.net/lleozhang/article/details/82935400

最后是官方题解的C++版本

using PII = pair<int, int>;	

class Solution {
public:
    bool canMeasureWater(int x, int y, int z) {
        stack<PII> stk;
        stk.emplace(0, 0);
        //用匿名函数重写hash函数
        auto hash_function = [](const PII& o) {return hash<int>()(o.first) ^ hash<int>()(o.second);};
        //decltype 返回表达式的数据类型
        unordered_set<PII, decltype(hash_function)> seen(0, hash_function);
        while (!stk.empty()) {
        	//之前入过栈
            if (seen.count(stk.top())) {
                stk.pop();
                continue;
            }
            //标记入栈
            seen.emplace(stk.top());
            //...没学过的特性
            auto [remain_x, remain_y] = stk.top();
            stk.pop();
            if (remain_x == z || remain_y == z || remain_x + remain_y == z) {
                return true;
            }
            // 把 X 壶灌满。
            stk.emplace(x, remain_y);
            // 把 Y 壶灌满。
            stk.emplace(remain_x, y);
            // 把 X 壶倒空。
            stk.emplace(0, remain_y);
            // 把 Y 壶倒空。
            stk.emplace(remain_x, 0);
            // 把 X 壶的水灌进 Y 壶,直至灌满或倒空。
            stk.emplace(remain_x - min(remain_x, y - remain_y), remain_y + min(remain_x, y - remain_y));
            // 把 Y 壶的水灌进 X 壶,直至灌满或倒空。
            stk.emplace(remain_x + min(remain_y, x - remain_x), remain_y - min(remain_y, x - remain_x));
        }
        return false;
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值