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,先写几个特例看看有没有规律
- z大于x+y的时候一定取不出,接下来的能取出的z都应该满足z<=(x+y)
- z为x或y的倍数的时候一定能取出
- z为y-x的时候一定能取出,进一步推断,z为y-x的倍数时候一定能取出,预测一下,当y-x=1的时候能取出所有满足1的z
- 考虑到有可能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;
}
};