> 文章列表 > Leetcode.365 水壶问题

Leetcode.365 水壶问题

Leetcode.365 水壶问题

题目链接

Leetcode.365 水壶问题 mid

题目描述

有两个水壶,容量分别为 xy升。水的供应是无限的。确定是否有可能使用这两个壶准确得到 z升。

如果可以得到 z升水,最后请用以上水壶中的一或两个来盛放取得的 z升水。

你可以:

  • 装满任意一个水壶
  • 清空任意一个水壶
  • 从一个水壶向另外一个水壶倒水,直到装满或者倒空

示例 1:

输入: jug1Capacity = 3, jug2Capacity = 5, targetCapacity = 4
输出: true
解释:来自著名的 “Die Hard”

示例 2:

输入: jug1Capacity = 2, jug2Capacity = 6, targetCapacity = 5
输出: false

示例 3:

输入: jug1Capacity = 1, jug2Capacity = 2, targetCapacity = 3
输出: true

提示:

  • 1<=x,y,z<=1061 <= x, y, z <= 10^61<=x,y,z<=106

解法一:bfs

由于题目给定的操作,我们可以将两个水壶看成一个整体,总的水量 ttt 看成是一个状态,每次操作之后只会有如下四个状态:

  • t+xt + xt+x (t+x≤x+y)(t + x \\leq x + y)(t+xx+y)
  • t+yt + yt+y (t+y≤x+y)(t + y \\leq x + y)(t+yx+y)
  • t−xt - xtx (t−x≥0)(t - x \\geq 0)(tx0)
  • t−yt - yty (t−y≥0)(t - y \\geq 0)(ty0)

我们就可以用从初始状态 0 开始(初始两个水壶都为空,故 t=0t = 0t=0bfs,在这个过程中,我们用一个哈希表记录 已经访问过的状态。如果存在一个状态 t′=zt' = zt=z,说明两个水壶可以得到 zzz 升水,故返回 true

bfs 结束,返回 false

时间复杂度: O(n)O(n)O(n)

C++代码:

class Solution {
public:bool canMeasureWater(int x, int y, int z) {if(z > x + y) return false;unordered_set<int> vis;queue<int> q;q.push(0);vis.insert(0);while(!q.empty()){auto t = q.front();q.pop();if(t == z) return true;if(t + x <= x + y && !vis.count(t + x)){vis.insert(t + x);q.push(t + x);}if(t + y <= x + y && !vis.count(t + y)){vis.insert(t + y);q.push(t + y);}if(t - x >= 0 && !vis.count(t - x)){vis.insert(t - x);q.push(t - x);}if(t - y >= 0 && !vis.count(t - y)){vis.insert(t - y);q.push(t - y);}}return false;}
};

解法二:裴蜀定理

对于题目给定的操作,我们可以认为每次操作只会让 两个水壶的总的水量 增加xxx , 减少 xxx ,增加 yyy,减少 yyy

  • 因为不会出现 两个桶同时有水并且都不是满的。题目给定的三种操作,无论怎么组合使用,一定至少会有一个桶为空 或者 满。
  • 对一个没满的桶加水是无意义的。因为如果另一个桶是空的,那么这个操作的结果等价于直接从初始状态给这个桶加满水;而如果另一个桶是满的,那么这个操作的结果等价于从初始状态分别给两个桶加满。
  • 另外,把一个不满的桶里面的水倒掉也是无意义的。因为如果另一个桶是空的,那么这个操作的结果等价于回到初始状态;而如果另一个桶是满的,那么这个操作的结果等价于从初始状态直接给另一个桶倒满。

所以实际上,每次操作只会给总的水量带来 xxx 或者 yyy 的变化量,可以用下面的式子表示:

ax+by=zax + by = z ax+by=z

即,裴蜀定理。

该式成立的条件是 zzz 是否能整除 gcd(a,b)gcd(a,b)gcd(a,b)

时间复杂度: O(log(min(x,y)))O(log(min(x,y)))O(log(min(x,y)))

C++代码:


class Solution {
public:bool canMeasureWater(int x, int y, int target) {if(target > x + y) return false;return target % gcd(x,y) == 0;}
};