[路飞]_前端算法第七十三弹-移除石子的最大得分

119 阅读3分钟

你正在玩一个单人游戏,面前放置着大小分别为 abc三堆 石子。

每回合你都要从两个不同的非空堆 中取出一颗石子,并在得分上加 1 分。当存在 两个或更多 的空堆时,游戏停止。

给你三个整数 abc ,返回可以得到的 最大分数

示例 1:

输入:a = 2, b = 4, c = 6
输出:6
解释:石子起始状态是 (2, 4, 6) ,最优的一组操作是:
- 从第一和第三堆取,石子状态现在是 (1, 4, 5)
- 从第一和第三堆取,石子状态现在是 (0, 4, 4)
- 从第二和第三堆取,石子状态现在是 (0, 3, 3)
- 从第二和第三堆取,石子状态现在是 (0, 2, 2)
- 从第二和第三堆取,石子状态现在是 (0, 1, 1)
- 从第二和第三堆取,石子状态现在是 (0, 0, 0)
总分:6 分 。

示例 2:

输入:a = 4, b = 4, c = 6
输出:7
解释:石子起始状态是 (4, 4, 6) ,最优的一组操作是:
- 从第一和第二堆取,石子状态现在是 (3, 3, 6)
- 从第一和第三堆取,石子状态现在是 (2, 3, 5)
- 从第一和第三堆取,石子状态现在是 (1, 3, 4)
- 从第一和第三堆取,石子状态现在是 (0, 3, 3)
- 从第二和第三堆取,石子状态现在是 (0, 2, 2)
- 从第二和第三堆取,石子状态现在是 (0, 1, 1)
- 从第二和第三堆取,石子状态现在是 (0, 0, 0)
总分:7 分 。

示例 3:

输入:a = 1, b = 8, c = 8
输出:8
解释:最优的一组操作是连续从第二和第三堆取 8 回合,直到将它们取空。
注意,由于第二和第三堆已经空了,游戏结束,不能继续从第一堆中取石子。

这道题我的想法是每次都从最多的和最少的堆中各取出一个,直到有两个以上的堆数量为0为止。所以我们这里需要用到堆进行排序。

我们可以用大顶堆也可以用小顶堆,我们这里用到的是小顶堆。我们先将三个堆存入小顶堆中,然后取出堆顶,为最小值,再取出堆顶为中间值,最后一个值为最大值,将第一个和最后一个减一,直到最小值为0时,对后两个数进行想减,直到有两个0结束。

/**
 * @param {number} a
 * @param {number} b
 * @param {number} c
 * @return {number}
 */
var maximumScore = function (a, b, c) {
    let heap = new Heap("small")
    heap.push(a)
    heap.push(b)
    heap.push(c)
    let nums = 0
    let min = heap.pop()
    while (min != 0 || heap.top() != 0) {
        nums++
        let mid = heap.pop()
        let max = heap.pop()
        if (min > 0) {
            min--, max--;
        } else {
            mid--, max--;
        }
        heap.push(min)
        heap.push(mid)
        heap.push(max)
        min = heap.pop()
    }
    return nums
};

小顶堆代码

class Heap {
    constructor(cmp = "large") {
        if (cmp == "large") {
            this.cmp = this.large;
        } else if (cmp == "small") {
            this.cmp = this.small
        } else {
            this.cmp = cmp
        }
        this.res = [];
        this.cnt = 0;
    }

    push(val) {
        this.cnt++;
        this.res.push(val)
        this.shiftUp(this.cnt - 1)
    }

    pop() {
        this.cnt--;
        const res = this.res[0]
        const pop = this.res.pop()
        if (this.cnt) {
            this.res[0] = pop
            this.shiftDown(0)
        }
        return res
    }

    shiftUp(i) {
        if (i === 0) return
        const par = this.getParentIndex(i)
        if (this.cmp(this.res[par], this.res[i])) {
            this.swap(par, i)
            this.shiftUp(par)
        }
    }

    shiftDown(i) {
        const l = this.getLeftIndex(i)
        const r = this.getRightIndex(i)
        if (l < this.cnt && this.cmp(this.res[i], this.res[l])) {
            this.swap(i, l)
            this.shiftDown(l)
        }
        if (r < this.cnt && this.cmp(this.res[i], this.res[r])) {
            this.swap(i, r)
            this.shiftDown(r)
        }
    }

    getParentIndex(i) {
        return (i - 1) >> 1
    }

    getLeftIndex(i) {
        return i * 2 + 1
    }

    getRightIndex(i) {
        return i * 2 + 2
    }

    large = (a, b) => a < b

    small = (a, b) => a > b;

    swap = (i, j) => [this.res[i], this.res[j]] = [this.res[j], this.res[i]];

    top = () => this.res[0];

    size = () => this.cnt;

    isEmpty = () => this.cnt === 0

}