有两个容量分别为 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/water-and-jug-problem
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
完整代码
基本思想:
BFS
需要确定水壶的六种状态:
- A空
- B空
- A满
- B满
- A向B倒水
- B向A倒水
在当前状态下,每一次都考虑下这六种状态,如果出现了:x + y = z的情况,说明成功了
class Solution {
public:
bool canMeasureWater(int x, int y, int z) {
queue<pair<int, int>> q;
unordered_set<pair<int, int>, myHash> s;//确保状态不会重复考虑
q.push({0, 0}); //初始情况下是0状态
s.insert({0, 0});
while(!q.empty()){
auto temp = q.front();
q.pop();
if(temp.first + temp.second == z)
return true;
for(int i = 0; i < 6; ++i){//针对当前的水量,考虑这6中状态
auto next = op(i, temp, x, y);
if(s.find(next) != s.end()){
continue;
}
s.insert(next);
q.push(next);
}
}
return false;
}
private:
struct myHash{//自定义的哈希函数
size_t operator()(const pair<int, int> &val) const {
return static_cast<size_t>(val.first) * 100000007 + val.second;
}
};
pair<int, int> op(int i, pair<int, int> temp, int x, int y){
switch(i){
case 0: return make_pair(x, temp.second);//A满
case 1: return make_pair(temp.first, y);//B满
case 2: return make_pair(0, temp.second);//A空
case 3: return make_pair(temp.first, 0);//B空
case 4: {//A向B倒水
int t = min(temp.first, y - temp.second);
return make_pair(temp.first - t, temp.second + t);
}
case 5:{//B向A倒水
//A中能盛B中的全部的水,还是只能盛A所需的那一部分水,二者取最小
int t = min(x - temp.first, temp.second);
return make_pair(temp.first + t, temp.second - t);
}
}
return make_pair(0, 0);
}
};
代码说明:上述代码中使用了unordered_set
- unordered_set内部实现是基于Hash实现,内部元素无序
- set内部实现是基于红黑树实现,内部元素有序
- 对比来看,如果不要求元素有序,只是为了保证元素不重复出现,用unordered_set效率高,因为unordered_set在对数据进行增删时时间复杂度是O(1),set是O(logn)
- 在使用unordered_set时,默认的Hash函数只支持string类型等少数类型,多数情况下需要自己定义哈希函数
数学思想
ax + by = z, 求x,y的最大公因数m, 如果z是m的倍数,那么可以达到目标,否则不行
- 求x , y的最大公因数
- 判断 z 是否是该最大公因数的倍数
class Solution {
public:
bool canMeasureWater(int x, int y, int z) {
if(z == 0)
return true;
if(x + y == 0 || x + y < z)
return false;
int m = __gcd(x, y);
return z % m == 0;//一定要注意:m不能为0
}
};
自己实现求最大公约数的代码
class Solution {
public:
bool canMeasureWater(int x, int y, int z) {
if(z == 0)
return true;
if(x + y == 0 || x + y < z)
return false;
int m = gcd(x, y);
return z % m == 0;
}
private:
int gcd(int x, int y){
if(x > y)
return gcd(y, x);
while(x){
int t = y % x;
y = x;
x = t;
}
return y;
}
};