LeetCode 1962. 移除石子使总数最小

1962. 移除石子使总数最小

给你一个整数数组 piles ,数组 下标从 0 开始 ,其中 piles[i] 表示第 i 堆石子中的石子数量。另给你一个整数 k ,请你执行下述操作 恰好 k 次:

  • 选出任一石子堆 piles[i] ,并从中 移除 floor(piles[i] / 2) 颗石子。

注意:你可以对 同一堆 石子多次执行此操作。

返回执行 k 次操作后,剩下石子的 最小 总数。

floor(x) 为 小于 或 等于 x 的 最大 整数。(即,对 x 向下取整)。

示例 1:

输入:piles = [5,4,9], k = 2
输出:12
解释:可能的执行情景如下:
- 对第 2 堆石子执行移除操作,石子分布情况变成 [5,4,5] 。
- 对第 0 堆石子执行移除操作,石子分布情况变成 [3,4,5] 。
剩下石子的总数为 12 。

示例 2:

输入:piles = [4,3,6,7], k = 3
输出:12
解释:可能的执行情景如下:
- 对第 2 堆石子执行移除操作,石子分布情况变成 [4,3,3,7] 。
- 对第 3 堆石子执行移除操作,石子分布情况变成 [4,3,3,4] 。
- 对第 0 堆石子执行移除操作,石子分布情况变成 [2,3,3,4] 。
剩下石子的总数为 12 。

提示:

  • 1 <= piles.length <= 10^5
  • 1 <= piles[i] <= 10^4
  • 1 <= k <= 10^5

提示 1

Choose the pile with the maximum number of stones each time.


提示 2

Use a data structure that helps you find the mentioned pile each time efficiently.


提示 3

One such data structure is a Priority Queue.

解法1:贪心 + 优先队列

思路

题目要求 k 次拿完后,剩下的石头数最少,可以贪心地每次都从石子数最多的堆里拿石头,然后将剩下的放回去。我们假设某一步操作没有选择石子数量最多的一堆(记为第 opt 堆)进行操作,而是选择了第 i 堆,那么后续可能有两种情况:

  • 第一种,如果后续的某一步中选择了第 opt 堆,那么我们可以交换这两步的操作,最终剩余的石子数目不变;
  • 第二种,如果后续没有移除过第 opt 堆的石子,那么将从当前开始到结束为止所有选择第 i 堆的操作全部换成第 opt 堆,总移除的石子数目也不会减少。

因此,每次选择石子数量最多的一堆进行操作是最优的。每次都要挑出石子数最多的堆,可以考虑用优先队列的数据结构,这样可以 O(logn) 的时间复杂度完成出队列和入队列,执行 k 次后返回剩下石子数总和。

Java版:

class Solution {
    public int minStoneSum(int[] piles, int k) {
        PriorityQueue<Integer> q = new PriorityQueue<Integer>((a, b) -> b - a);
        for (int pile : piles) {
            q.offer(pile);
        }
        while (k > 0) {
            int x = q.poll();
            x -= x / 2;
            q.offer(x);
            k--;
        }
        int ans = 0;
        while (!q.isEmpty()) {
            ans += q.poll();
        }
        return ans;
    }
}

Python3版:

class Solution:
    def minStoneSum(self, piles: List[int], k: int) -> int:
        q = [-p for p in piles]
        heapify(q)
        while k > 0:
            x = -heappop(q)
            x -= x // 2
            heappush(q, -x) 
            k -= 1
        return -sum(q)

复杂度分析

  • 时间复杂度:O(k×logn+n),其中 n 是数组 piles 的长度。将数组变为优先队列消耗 O(n),k 次入队列和出队列的操作,每次消耗 O(logn),总的时间复杂度是 O(k×logn+n)。
  • 空间复杂度:O(n),新建一个优先队列消耗 O(n)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值