Easy
题目描述
给你一个整数数组 gifts ,表示各堆礼物的数量。每一秒,你需要执行以下操作:
- 选择礼物数量最多的那一堆。
- 如果不止一堆礼物数量最多,你可以选择任意一堆。
- 将这堆礼物的数量减少为原来数量的平方根的向下取整。
返回 k 秒后剩余的礼物数量。
示例 1:
输入:gifts = [25,64,9,4,100], k = 4
输出:29
解释:
按下述方式取礼物:
- 第 1 秒,选择最后一堆,留下 10 个礼物。
- 第 2 秒,选择第二堆,留下 8 个礼物。
- 第 3 秒,选择第一堆,留下 5 个礼物。
- 第 4 秒,选择最后一堆,留下 3 个礼物。
最终剩下的礼物为 [5,8,9,4,3] ,所以总共剩余礼物数量为 29 。
示例 2:
输入:gifts = [1,1,1,1], k = 4
输出:4
解释:
在这种情况下,不管选择哪一堆,每一堆都只能留下 1 个礼物。
也就是说,你无法获取任何一堆的礼物。
所以,剩余礼物的总数量是 4 。
提示:
1 <= gifts.length <= 10³1 <= gifts[i] <= 10⁹1 <= k <= 10³
解题思路
这道题需要我们在 k 秒内不断找到最大值并将其替换为其平方根的向下取整。
解题思路:
最优解法 - 最大堆(优先队列):使用最大堆来维护礼物数量,这样每次都能快速找到最大值。每次操作后将更新的值重新放入堆中,时间复杂度为 O(k log n)。
暴力解法:每次遍历数组找最大值并更新,时间复杂度为 O(k * n)。
推荐使用最大堆解法,因为它在处理大量操作时更高效。具体步骤:
- 将所有礼物数量放入最大堆
- 重复 k 次:取出堆顶最大值,计算其平方根向下取整,再放回堆中
- 最后计算堆中所有元素的和
注意点:
- 使用
sqrt()函数计算平方根,并用强制类型转换取整 - 最终结果可能较大,使用
long long类型存储 - 各语言的堆实现略有不同,需要相应调整
代码实现
class Solution {
public:
long long pickGifts(vector<int>& gifts, int k) {
priority_queue<int> pq(gifts.begin(), gifts.end());
for (int i = 0; i < k; i++) {
int maxGifts = pq.top();
pq.pop();
pq.push((int)sqrt(maxGifts));
}
long long result = 0;
while (!pq.empty()) {
result += pq.top();
pq.pop();
}
return result;
}
};
class Solution:
def pickGifts(self, gifts: List[int], k: int) -> int:
import heapq
import math
# Python的heapq是最小堆,所以存储负值来模拟最大堆
heap = [-gift for gift in gifts]
heapq.heapify(heap)
for _ in range(k):
max_gifts = -heapq.heappop(heap)
heapq.heappush(heap, -int(math.sqrt(max_gifts)))
return -sum(heap)
public class Solution {
public long PickGifts(int[] gifts, int k) {
var pq = new PriorityQueue<int, int>(Comparer<int>.Create((a, b) => b.CompareTo(a)));
foreach (int gift in gifts) {
pq.Enqueue(gift, gift);
}
for (int i = 0; i < k; i++) {
int maxGifts = pq.Dequeue();
int newGifts = (int)Math.Sqrt(maxGifts);
pq.Enqueue(newGifts, newGifts);
}
long result = 0;
while (pq.Count > 0) {
result += pq.Dequeue();
}
return result;
}
}
var pickGifts = function(gifts, k) {
// JavaScript没有内置堆,使用数组模拟
let heap = [...gifts];
for (let i = 0; i < k; i++) {
// 找到最大值的索引
let maxIdx = 0;
for (let j = 1; j < heap.length; j++) {
if (heap[j] > heap[maxIdx]) {
maxIdx = j;
}
}
// 更新最大值为其平方根的向下取整
heap[maxIdx] = Math.floor(Math.sqrt(heap[maxIdx]));
}
return heap.reduce((sum, gift) => sum + gift, 0);
};
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 最大堆(推荐) | O(n + k log n) | O(n) |
| 暴力遍历 | O(k × n) | O(1) |
- 时间复杂度:最大堆解法中,建堆需要 O(n),k 次操作每次需要 O(log n)
- 空间复杂度:需要额外的堆空间存储所有元素