【算法】【C++、贪心】力扣1962. 移除石子使总数最小

原题链接


【算法】力扣1962. 移除石子使总数最小

问题描述

给定一个整数数组 piles,其中 piles[i] 表示第 i 堆石子中的石子数量。同时给定整数 k,要求执行恰好 k 次操作:

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

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

注:可以对同一堆石子多次执行此操作。floor(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。

解决方案

这是一道简单的贪心题,只要每次对最多石头的堆进行移除即可。我们将分别使用两种数据结构,即 multisetpriority_queue(最大堆),来解决此问题。以下是两种解法的完整代码以及步骤解析。

使用 multiset 解决问题

class Solution {
public:
    int minStoneSum(vector<int>& piles, int k) {
        multiset<int> mset;
        for (auto sto : piles) {
            mset.insert(sto);
        }
        while (k--) {
            auto it = mset.end();
            --it;
            int x = (*it + 1) / 2;
            mset.erase(it);
            mset.insert(x);
        }
        int ans = 0;
        for (auto it = mset.begin(); it != mset.end(); ++it) {
            ans += *it;
        }
        return ans;
    }
};

步骤解析:

  1. 创建一个 multiset 数据结构,将石子堆中的石子数量插入其中。
  2. 循环执行 k 次操作,每次从 multiset 中取出最大值,执行移除操作,并将新的值插入 multiset
  3. 计算剩下石子的总数并返回。

使用 priority_queue 解决问题

class Solution {
public:
    int minStoneSum(vector<int>& piles, int k) {
        int ans = 0;
        priority_queue<int> pq;
        for (auto sto : piles) {
            ans += sto;
            pq.push(sto);
        }
        while (k--) {
            int mx = pq.top();
            int remove_pile = mx / 2;
            ans -= remove_pile;
            pq.pop();
            pq.push(mx - remove_pile);
        }
        return ans;
    }
};

步骤解析:

  1. 创建一个 priority_queue(最大堆)数据结构,将石子堆中的石子数量插入其中,并计算石子总数。
  2. 循环执行 k 次操作,每次从 priority_queue 中取出最大值,执行移除操作,并将新的值插入 priority_queue
  3. 计算剩下石子的总数并返回。

两种数据结构的解析

priority_queue 数据结构解析

priority_queue 是一个优先队列,基于堆结构实现。在 C++ 中,默认情况下,priority_queue 是最大堆,即堆顶元素是队列中的最大元素。在本问题中,我们使用 priority_queue 来维护石子堆的最大值,以便能够高效地执行移除操作。

步骤解析:

  1. 初始化: 创建一个 priority_queue 对象,将石子堆中的石子数量插入其中,同时计算石子总数。
  2. 循环操作: 执行 k 次操作,每次从 priority_queue 中取出最大值,即当前堆的最大石子数量。
  3. 移除操作: 计算需要移除的石子数量(即当前最大值的一半),并更新石子总数。
  4. 更新队列: 将更新后的石子数量插入 priority_queue 中,保持队列的最大堆性质。

通过这样的操作,我们可以高效地找到并处理每次操作的目标石子堆,从而得到剩下石子的最小总数。

multiset 数据结构解析

multiset 是 C++ 标准模板库中的关联容器,是一个有序集合,允许元素重复。在本问题中,我们使用 multiset 来实现一个有序的石子堆,以便能够方便地找到并处理最大值。

步骤解析:

  1. 初始化: 创建一个 multiset 对象,将石子堆中的石子数量插入其中,形成有序集合。
  2. 循环操作: 执行 k 次操作,每次从 multiset 中取出最大值,即当前堆的最大石子数量。
  3. 移除操作: 计算需要移除的石子数量(即当前最大值的一半),并更新石子总数。
  4. 更新集合: 移除当前最大值,将更新后的石子数量插入 multiset 中,保持有序性。

通过这样的操作,我们可以在 multiset 中高效地找到并处理每次操作的目标石子堆,得到剩下石子的最小总数。

  • 22
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值