365. 水壶问题
题目:
有两个容量分别为 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
解答:
数学方法不考虑,只说正常解法。想一下当前我们的两个水壶水量为cur_x,cur_y。我们下一步有几种做法:(1):装满水壶1或者2 (2):清空水壶1或2 (3):水壶1给2倒水/水壶2给1倒水
那么显然要一步步搜索可能的所有状态,那么是用DFS还是BFS呢?显然我们只要找到了一个解就可以返回。考虑一下要求的解,不像是那种递归到底才能找到的解,而是一个经过有限次就能够达到的解。所以考虑用BFS,这样经过少数轮次就应该可以找到解。
当然对于计算过的状态,必须用一个状态表记录下来。我们使用unordered_map< pair<int,int> >来存储状态,但pair<int,int>不是基本类型,我们需要自己写对应的hash函数,不懂的可以看这里:https://www.cnblogs.com/FdWzy/p/12542840.html
代码:
class Solution { public: struct hs{ size_t operator()(const pair<int,int>& x)const{ return hash<int>()(x.first) ^ hash<int>()(x.second); } }; bool canMeasureWater(int x, int y, int z) { if(x+y<z){ return false; } unordered_set<pair<int,int>,hs> visited; int cur_x=x,cur_y=y; queue<pair<int,int> > que; que.push(make_pair(cur_x,cur_y)); visited.insert(make_pair(cur_x,cur_y)); while(not que.empty()){ cur_x=que.front().first,cur_y=que.front().second; que.pop(); if(cur_x==z or cur_y==z or cur_x+cur_y==z){ return true; } // cout<<cur_x<<" "<<cur_y<<endl; //装满x或y: if(visited.count(make_pair(x,cur_y)) == 0){ que.push(make_pair(x,cur_y)); visited.insert({x,cur_y}); } if(visited.count(make_pair(cur_x,y)) == 0){ que.push(make_pair(cur_x,y)); visited.insert({cur_x,y}); } //清空x或y: if(visited.count(make_pair(0,cur_y)) == 0){ que.push(make_pair(0,cur_y)); visited.insert({0,cur_y}); } if(visited.count(make_pair(cur_x,0)) == 0){ que.push(make_pair(cur_x,0)); visited.insert({cur_x,0}); } //x给y倒水 或 y给x倒水 int x_to_y=min(cur_x,y-cur_y),y_to_x=min(cur_y,x-cur_x); if(visited.count(make_pair(cur_x-x_to_y,cur_y+x_to_y)) == 0){ que.push(make_pair(cur_x-x_to_y,cur_y+x_to_y)); visited.insert({cur_x-x_to_y,cur_y+x_to_y}); } if(visited.count(make_pair(cur_x+y_to_x,cur_y-y_to_x)) == 0){ que.push(make_pair(cur_x+y_to_x,cur_y-y_to_x)); visited.insert({cur_x+y_to_x,cur_y-y_to_x}); } } return false; } };
进击的小🐴农