题目:
2558. 从数量最多的堆取走礼物
给你一个整数数组 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
解答:
方法1:
暴力法:先排序,再将最大的那个进行平方
方法2:堆
代码:
方法1:
class Solution {
public long pickGifts(int[] gifts, int k) {
int n=gifts.length;
for(int i=0;i<k;i++){
Arrays.sort(gifts);
gifts[n-1]=(int)Math.floor(Math.sqrt(gifts[n-1]));
}
long sum=0;
for(int i=0;i<n;i++){
sum+=gifts[i];
//System.out.println(gifts[i]);
}
return sum;
}
}
方法2:
class Solution {
int[] heap = new int[10010];
int sz = 0;
public long pickGifts(int[] gs, int k) {
for (int x : gs) add(-x);
while (k-- > 0) add(-(int)Math.sqrt(-pop()));
long ans = 0;
while (sz != 0) ans += -heap[sz--]; // 没必要再维持堆的有序, 直接读取累加
return ans;
}
void swap(int a, int b) {
int c = heap[a];
heap[a] = heap[b];
heap[b] = c;
}
void up(int u) {
// 将「当前节点 i」与「父节点 i / 2」进行比较, 若父节点值更大, 则进行交换
int fa = u / 2;
if (fa != 0 && heap[fa] > heap[u]) {
swap(fa, u);
up(fa);
}
}
void down(int u) {
// 将当「前节点 cur」与「左节点 l」及「右节点 r」进行比较, 找出最小值, 若当前节点不是最小值, 则进行交换
int cur = u;
int l = u * 2, r = u * 2 + 1;
if (l <= sz && heap[l] < heap[cur]) cur = l;
if (r <= sz && heap[r] < heap[cur]) cur = r;
if (cur != u) {
swap(cur, u);
down(cur);
}
}
void add(int x) {
heap[++sz] = x;
up(sz);
}
int peek() {
return heap[1];
}
int pop() {
int ans = peek();
heap[1] = heap[sz--];
down(1);
return ans;
}
}