题目:
有两个水壶,容量分别为
x
和y
升。水的供应是无限的。确定是否有可能使用这两个壶准确得到target
升。你可以:
- 装满任意一个水壶
- 清空任意一个水壶
- 将水从一个水壶倒入另一个水壶,直到接水壶已满,或倒水壶已空。
示例 1:
输入: x = 3,y = 5,target = 4 输出: true 解释: 按照以下步骤操作,以达到总共 4 升水: 1. 装满 5 升的水壶(0, 5)。 2. 把 5 升的水壶倒进 3 升的水壶,留下 2 升(3, 2)。 3. 倒空 3 升的水壶(0, 2)。 4. 把 2 升水从 5 升的水壶转移到 3 升的水壶(2, 0)。 5. 再次加满 5 升的水壶(2, 5)。 6. 从 5 升的水壶向 3 升的水壶倒水直到 3 升的水壶倒满。5 升的水壶里留下了 4 升水(3, 4)。 7. 倒空 3 升的水壶。现在,5 升的水壶里正好有 4 升水(0, 4)。 参考:来自著名的 "Die Hard"
示例 2:
输入: x = 2, y = 6, target = 5 输出: false
示例 3:
输入: x = 1, y = 2, target = 3 输出: true 解释:同时倒满两个水壶。现在两个水壶中水的总量等于 3。
提示:
1 <= x, y, target <= 103
思路:
假设是水壶a和水壶b,一共有以下六种情况:
- 装满任意一个水壶(a装满,b装满)
- 清空任意一个水壶(a清空,b清空)
- 将水从一个水壶倒入另一个水壶(a倒入b,b倒入a)
第三种需要考虑原水壶的水空了和倒入水的水壶满了的情况,所以需要判断一下每次需要倒的水量。
初始的a和b为0,[0,0]先入栈,然后每次取出栈中的第一个元素并让它出栈,分析它的这六种情况,将可行的情况入栈。
并记录每次出栈的元素,就是已经出现过的水壶状态,将状态记入set中。
如果栈中第一个元素为在set集合中存在的元素,则出栈,continue,继续下一次寻找。
函数结束的条件是a==target||b==target||a+b==target,就是这两个壶准确得到
target
升结束。如果栈为空了,还是没有满足函数结束条件,循环结束后返回false。
这里面有一个注意点,就是如果set集合中放的是[0,0]这种的数组,那么set.has比较的是数组的原地址,而不是数组中的值。所以在这里set.add(''+[a,b]),我在集合中存放的是字符串,这样就可以用set.has进行比较了。
代码:
/**
* @param {number} x
* @param {number} y
* @param {number} target
* @return {boolean}
*/
var canMeasureWater = function(x, y, target) {
let stack = [[0,0]]
let set = new Set()
while(stack.length>0){
// 出栈,取出a和b的值
let [a,b] = stack.shift()
// 循环结束条件
if(a==target||b==target||a+b==target) return true
// 已经出现过的水壶状态,进入下次循环
if(set.has(''+[a,b])) continue
//以下为分析中的六种情况
if(a<x) stack.push([x,b])
if(b<y) stack.push([a,y])
if(a!=0) {
stack.push([0,b])
if(b!=y) {
let diff = a+b<y?a:y-b
stack.push([a-diff,b+diff])
}
}
if(b!=0) {
stack.push([a,0])
if(a!=x) {
let diff = a+b<x?b:x-a
stack.push([a+diff,b-diff])
}
}
// 将这次的[a,b]加入set
set.add(''+[a,b])
}
return false
};