~/algorithm/

365. 水壶问题

#leetcode#algorithm#js

题目描述

有两个容量分别为 x 升 和 y 升 的水壶以及无限多的水。请判断能否通过使用这两个水壶,从而可以得到恰好 z 升 的水?

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

你允许:

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

输入: (x = 3), (y = 5), (z = 4);
输出: True;

示例 2:

输入: (x = 2), (y = 6), (z = 5);
输出: False;

解题思路

最开始的思路是比较 x 和 y 的大小,用大减去小,差值在和 x,y 进行加减,一直减到 0,然后再和 z 进行比较。然后超时了,是两个很大,但是差值很小的数,这样的话中间结果太多,导致了超时,于是将相减改为,取余数。最后看 z 能否整除中间的结果。(584ms。。。)

解法优化

提交后,看了前几名答案,都是在寻找最大公约数,看结果能否整除最大公约数。。。(100ms 左右)

代码展示

/**
 * @param {number} x
 * @param {number} y
 * @param {number} z
 * @return {boolean}
 */
var canMeasureWater = function(x, y, z) {
  if (z == 0) return true;
  if (x + y < z) return false;
  let fun = (a, b, arr) => {
    let max, min, minus;
    max = Math.max(a, b);
    min = a + b - max;
    minus = max - min;
    do {
      arr.push(minus);
      arr = arr.concat(
        arr.map(ele => ele + minus),
        arr.map(ele => ele - minus)
      );
      arr = [...new Set(arr)];
      max = Math.max(min, minus);
      min = Math.min(min, minus);
      //最初这里是减号minus = max - min;
      minus = max % min;
    } while (min != minus && min != 0);

    return arr;
  };
  console.log(fun(x, y, [x, y]));
  return fun(x, y, [x, y]).some(ele => z % ele == 0);
};

改进后的代码

/**
 * @param {number} x
 * @param {number} y
 * @param {number} z
 * @return {boolean}
 */
var canMeasureWater = function(x, y, z) {
  let gcd = (a, b) => (!b ? a : gcd(b, a % b));
  if (x + y == z) return true;
  return x + y >= z && z % gcd(x, y) == 0;
};