leetcode365.水壶问题

有两个容量分别为 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;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章