🌈【LeetCode 1648. 销售价值减少的颜色球 】- JavaScript(贪心+二分)

245 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情


说明:文章部分内容及图片出自网络,如有侵权请与我本人联系(主页有公众号:小攻城狮学前端)

作者:小只前端攻城狮、 主页:小只前端攻城狮的主页、 来源:掘金

GitHub:P-J27、 CSDN:PJ想做前端攻城狮

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


【LeetCode 1648. 销售价值减少的颜色球 】- JavaScript(贪心+二分)

题意描述

你有一些球的库存 inventory ,里面包含着不同颜色的球。一个顾客想要 任意颜色 总数为 orders 的球。

这位顾客有一种特殊的方式衡量球的价值:每个球的价值是目前剩下的 同色球 的数目。比方说还剩下 6 个黄球,那么顾客买第一个黄球的时候该黄球的价值为 6 。这笔交易以后,只剩下 5 个黄球了,所以下一个黄球的价值为 5 (也就是球的价值随着顾客购买同色球是递减的)

给你整数数组 inventory ,其中 inventory[i] 表示第 i 种颜色球一开始的数目。同时给你整数 orders ,表示顾客总共想买的球数目。你可以按照 任意顺序 卖球。

请你返回卖了 orders 个球以后 最大 总价值之和。由于答案可能会很大,请你返回答案对 109 + 7 取余数 的结果。

示例 1:

输入:inventory = [2,5], orders = 4 输出:14 解释:卖 1 个第一种颜色的球(价值为 2 ),卖 3 个第二种颜色的球(价值为 5 + 4 + 3)。 最大总和为 2 + 5 + 4 + 3 = 14 。

示例 2:

输入:s = "aaabbbcc" 输出:2 解释:可以删除两个 'b' , 得到优质字符串 "aaabcc" 。 另一种方式是删除一个 'b' 和一个 'c' ,得到优质字符串 "aaabbc" 。

思路分析:

第一眼看完题目,想到的就是贪心只要每次都卖出剩下同色球数目最多的球,就能够得到卖了 orders 个球之后最大总价值之和。但是贪心可能不够快,发现题目还是满足二分的但是二分的下界不好判断。需要好好想一下。不能蛮上,不然只能一直Wa~

二分查找

我们把整个过程想象成整组数字从上到下一层一层的取,直到取到orders数量为止。把我们可以取到的最小的数字进行做二分法,如果数列中这个数字这个数字以上的部分全部取完小于orders说明还要取更小的数字,否则需要取得更大的数字。答案很可能存在于两层之间,也就是这一层取完大于orders,上一层取完小于orders,所以验证时同时计算两层。

注意: 不过这里有一个坑,如果当前最低价值计算可获取订单数量小于预订订单数量时,需要将最低价值-1,继续遍历数组累加利润,直到可获取订单数量==预订订单数量,这样得到的才是最大利润。

var maxProfit = function(inventory, orders) {
    const mod = BigInt(1e9 + 7);
    inventory.sort((a, b) => (b - a));
    let l = 0, r = 1e9, len = inventory.length;
    while (l < r) {
        let mid = Math.floor((l + r) / 2), sum_ball = 0;
        
        for (let i = 0; i < len; i++) {
            sum_ball += Math.max(0, inventory[i]-mid+1);
            if (sum_ball > orders) break;
        }
        if (sum_ball < orders) {
            r = mid;
        } else {
            l = mid + 1;
        }
    }
    let res = 0n, sum_ba = 0;
    for (let i = 0; i < len; i++) {
        if (inventory[i] >= l) {
            res += BigInt(inventory[i]+ l) * BigInt(inventory[i] - l + 1) / 2n ;
            sum_ba += (inventory[i]  - l + 1);
            // res %= mod;
        } else {
            break;
        }
    }
    res += BigInt((orders - sum_ba) * (l - 1)) ;
    return res % mod;
    
};

贪心

思路:

贪心策略就是选择当前最大的值,并且每轮都选择当前最大值,最后也能得到最优解。这可以想到可以用优先队列每次取出最大的值,然后减去1再放回队列,由于值最大是1e9,这样的做法是超时的,所以需要考虑优化减1这个迭代的过程,即当前值最终会变成什么值。

具体:将库存inventory按降序排列,依次取价值最大的球,直到不能取为止。

var maxProfit = function (inventory, orders) {
    inventory.sort((a, b) => b - a)
    let i = 0
    let res = BigInt(0)
    let min = 0
    let flag = true

    while (flag) {
        while (inventory[i] === inventory[i + 1]) {
            i++
        }
        let v = (inventory[i] - inventory[i + 1]) * (i + 1)
        if (orders > v && i < inventory.length - 1) {
            orders -= v
            i++
        } else {
            let n = Math.floor(orders / (i + 1))
            min = inventory[i] - n
            orders -= n * (i + 1)
            flag = false
        }
    }
    while (i >= 0) {
        res += geth(inventory[i], inventory[i] - min)
        i--
    }
    return (res + BigInt(orders * min)) % BigInt(1e9 + 7)
};

function geth(num, orders) {
    return BigInt(num + num - orders + 1) * BigInt(orders) / BigInt(2) % BigInt(1e9 + 7)
}

感谢阅读,希望能对你有所帮助,文章若有错误或者侵权,可以在评论区留言或在我的主页添加公众号联系我。

写作不易,如果觉得不错,可以「点赞」+「评论」 谢谢支持❤