2558. 从数量最多的堆取走礼物

发布时间 2023-10-28 11:47:51作者: DawnTraveler

1.题目介绍

给你一个整数数组 gifts ,表示各堆礼物的数量。每一秒,你需要执行以下操作:
选择礼物数量最多的那一堆。
如果不止一堆都符合礼物数量最多,从中选择任一堆即可。
选中的那一堆留下平方根数量的礼物(向下取整),取走其他的礼物。
返回在 k 秒后剩下的礼物数量。

示例 1:
输入:gifts = [25,64,9,4,100], k = 4
输出:29
解释:
按下述方式取走礼物:

  • 在第一秒,选中最后一堆,剩下 10 个礼物。
  • 接着第二秒选中第二堆礼物,剩下 8 个礼物。
  • 然后选中第一堆礼物,剩下 5 个礼物。
  • 最后,再次选中最后一堆礼物,剩下 3 个礼物。
    最后剩下的礼物数量分别是 [5,8,9,4,3] ,所以,剩下礼物的总数量是 29 。

示例 2:
输入:gifts = [1,1,1,1], k = 4
输出:4
解释:
在本例中,不管选中哪一堆礼物,都必须剩下 1 个礼物。
也就是说,你无法获取任一堆中的礼物。
所以,剩下礼物的总数量是 4 。

提示:
1 <= gifts.length <= 103
1 <= gifts[i] <= 109
1 <= k <= 103

2.题解

具体关于堆的知识可参考:C++ 语法结构--堆

2.1 堆(使用优先级队列创建)

priority_queue 可以提供堆没有的优势,它可以自动保持元素的顺序;但我们不能打乱 priority_queue 的有序状态,因为除了第一个元素(top),我们无法直接访问它的其他元素。
使用一个最大堆维护各堆礼物的数量,弹出k次最大值,并进行平方根处理后,再重新插入最大堆。最后,最大堆中所有礼物的数量之和就是我们要返回的答案

class Solution {
public:
    long long pickGifts(vector<int>& gifts, int k) {
        priority_queue<int> p(gifts.begin(), gifts.end());
        while (k--){
            int num = p.top();
            p.pop();
            p.push(int(sqrt(num)));
        }
        // 求和
        long long res = 0;
        while (!p.empty()){
            res += p.top();
            p.pop();
        }
        return res;
    }
};

2.2 堆(使用C++内置的make_heap函数)

思路

使用 make_heap() 创建的堆可以提供一些 priority_queue 没有的优势:

  1. 可以访问堆中的任意元素,而不限于最大的元素,因为元素被存储在一个容器中,就像是我们自己的 vector。这也提供了偶然破坏元素顺序的可能,但是总可以调用 make_heap()来还原堆。
  2. 可以在任何提供随机访问迭代器的序列容器中创建堆。这些序列容器包括普通数组string 对象自定义容器。这意味着无论什么时候需要,都可以用这些序列容器的元素创建堆,必要时,可以反复创建。甚至还可以为元素的子集创建堆。

这里

代码

class Solution {
public:
    long long pickGifts(vector<int>& gifts, int k) {
        make_heap(gifts.begin(), gifts.end());
        for (int i = 0; i < k ;i ++) {
            pop_heap(gifts.begin(), gifts.end());
            *gifts.rbegin() = (int)(sqrt(*gifts.rbegin()));
            push_heap(gifts.begin(), gifts.end());
        }
        return accumulate(gifts.begin(), gifts.end(), 0ll);
    }
};