[路飞]移除石子的最大得分

151 阅读2分钟

记录 1 道算法题

移除石子的最大得分

leetcode-cn.com/problems/ma…


要求:

* 从三堆石头里面选择两堆,然后从这两堆里面分别拿走一个石头,然后计一分。

* 当石头出现两堆或以上,数量为 0 的时候停止。

* 返回最大总分

暴力解法

经过观察发现,只要我们每次取三堆石头里面的最大值和最小值,就可以得到最大的分数。分数说白了就是能够取多少次。

    function maximumScore(a, b, c) {
        let arr = [a, b, c]
        let count = 0
        // 每次判断数组是不是还有 2 个以上。0 会被剔除
        while ((arr = arr.filter(_ => _ > 0)).length >= 2) {
          let i = arr[0], ii
          // 判断如果数组每一个值都一样,就默认扣前两个
          // 之所以有这一层是因为 Math.min 和 Math.max 会得到相同的下标
          if (arr.filter(_ => _ === i).length === arr.length) {
            i = 0
            ii = 1
          } else {
              // 最大值 最小值
            i = arr.indexOf(Math.min(...arr))
            ii = arr.indexOf(Math.max(...arr))
          }
            
            // 计数,计分
          arr[i]--
          arr[ii]--
          count++
        }

        return count
    }

接下来是优化的解法

我们又发现了一个规律似乎将石子堆升序排列之后,前两个堆的和跟结果很相近。

例如: 2 4 6 --> 6, 1 8 8 --> 8, 1 1 1 --> 1

似乎是前两个堆的和如果小于等于第三个堆,就直接返回前两个堆的和。如果前两个堆的和大于第三个堆就是前两个堆的和 - 1。

于是就尝试了一下,结果不完全对,遇到这样一个测试用例, 24 19 24 --> 33。好像大于的时候不是单纯的 - 1。

但是前两个和小于等于第三个时的规律是正确的。问题在于大于第三个的时候的规律是什么。

于是去看了看题解,原来是只要把大于第三个的情况转成小于等于的情况就可以了。

假设有 a, b, c 三个石头堆,升序排列,已知 a + b <= c 时,结果是 a + b。

当 a + b > c 的时候,a + b - c 就是前两个和第三个相比,超出来的部分。如果我们让 a - n, b - n, 最终使 (a-n) + (b-n) <= c。那么这种情况下,结果就是前两个的和,当然还要加上 n。

还是用上面例子, 19 24 24

19 + 24 - 24 = 19,超出了19个,假如我们要在前两个里面减去 19 个石子。最好的办法就是平均分配,大家都减去 19 / 2。就是取前面两个堆拿出 19 / 2 次石子,加 19 / 2 分。

于是就变成了这样 10 15 24, 但是好像 10 + 15 多出了一个,再取一次吧。

于是最后 9 14 24,符合小于等于规律,结果是 9 + 14, 我们再加上之前取的 19 / 2 次和 1 次。

答案就是 9 + 14 + 9 + 1 = 33。

我们会发现当多出的部分是奇数的时候,我们需要多取一次,所以其实是 Math.ceil(19 / 2)

完整代码如下:

    function maximumScore(a, b, c) {
        let arr = [a, b, c]
        // 用的冒泡排序
        for (let i = 0; i < arr.length - 1; i++) {
          for (let j = i + 1; j < arr.length; j++) {
            if (arr[i] > arr[j]) {
              const temp = arr[i]
              arr[i] = arr[j]
              arr[j] = temp
            }
          }
        }
        
        // a + b
        const count = arr[0] + arr[1]
        // 前两个和 与 第三个的关系
        if (count > arr[2]) {
            // 超出了的要分的次数 k
          const k = Math.ceil((count - arr[2]) / 2)
          // 前面 (a-k) + (b-k) + k 的简化式子
          return count - k
        } else {
            // 小于等于直接返回
          return count
        }
    }

结束