[dfs] [数学] LeetCode 365. 水壶问题

[dfs] [数学] LeetCode 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

最开始摸拟了一些情况,找两个壶的最大值,让小的给大的灌水,最后会有溢出的水,比如3给5灌水,最后会溢出1,也就是小的能留下1的水,把大的倒空,1进大的在到一杯小的就得到4,然后巴拉巴拉记录从两个状态能导出的一些值,用背包那种思想f[i]表示水为i能不能达到,搞了一个小时能过一半,发现大的也能给小的灌水,是真的傻

dfs

最后总结一下发现只有六种操作可以选

填满x 填满y

清空x 清空y

把x剩余的倒入y 把y剩余的倒入x

所以暴力dfs,因为可能会有重复,所以用一个unordered_set保存出现过的情况,因为递归的过程可能过多会爆栈,就用一个栈来存储摸拟,当然时间复杂度和空间复杂度双双爆炸

官方题解:

using PII = pair<int, int>;

class Solution {
public:
    bool canMeasureWater(int x, int y, int z) {
        stack<PII> stk;
        //11新特性,在插入时直接构造,效率更高
        stk.emplace(0,0);
        //unordered_set基于hash实现,
        //只支持的build-in类型和string类型,别的类型要自定义hash函数,定义一个lambda表达式
        auto hash_function = [](const PII& rhs){return hash<int>()(rhs.first) ^ hash<int>()(rhs.second);};
        //decltype推测类型,和auto有一些区别,有一点就是不会真正去执行
        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());
            //17特性,大佬告诉我叫结构化绑定声明,和解包差不多
            auto [remian_x, remian_y] = stk.top();
            stk.pop();
            if(remian_x == z || remian_y == z || remian_x + remian_y == z) return true;

            //填满x
            stk.emplace(x, remian_y);
            //填满y
            stk.emplace(remian_x, y);
            //清空x
            stk.emplace(0, remian_y);
            //清空y
            stk.emplace(remian_x, y);
            //把x剩余的倒入y
            stk.emplace(remian_x - min(remian_x, y - remian_y), remian_y + min(remian_x, y - remian_y));
            //把y剩余的倒入x
            stk.emplace(remian_x + min(remian_y, x - remian_x), remian_y - min(remian_y, x - remian_x));
        }
        return false;
    }
};

数学解法

这就比较厉害了,就不怎么懂了

也是一个整体的思想,从总水量来看,只有四种情况:

增加x,增加y,减少x,减少y

剩下的情况比如往不满的壶里加水,这是没有意义的,因为只能加满

把一个不满的倒空,这也是没有意义的

因为只有x或者y的变化,这样问题就转化成能不能找到两个整数a和b使

ax + by = z

满足x+y>=z,并且ab存在

对于a>=0,b>=0,显然成立,非0时应该就是a=b=1应该

对于a<0

也就是需要倒掉一些a体积的水来达到,以样例3,5,4为例

3 * (-2) + 5 * 2 = 4 减两次3,加两次5

5装满倒进3,5剩2,倒掉3,5的2进3,5装满,倒进3,5剩4,倒掉3

总结下来就是

1.往y壶倒水

2.把y壶倒进x种

3.如果y不空,那么x一定是满的,把x的水倒掉然后把y的水倒进x

重复知道x倒空a次,y倒空b次

b小于0同理

贝祖定理告诉我们,ax+by=z 有解当且仅当 z 是 x, y的最大公约数的倍数。因此我们只需要找到 x, y的最大公约数并判断 z是否是它的倍数即可。

这我就不懂了记住就得了

class Solution {
public:
    int gcd(int a, int b){
        return b == 0 ? a : gcd(b, a % b);
    }
    bool canMeasureWater(int x, int y, int z) {
        if(x + y < z) return false;
        //对付101,100这样的情况
        if(!x || !y) return z == 0 || x + y == z;
        return z % gcd(x, y) == 0;c
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值