有兩個容量分別爲 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;
}
};