【算法】力扣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。
解决方案
这是一道简单的贪心题,只要每次对最多石头的堆进行移除即可。我们将分别使用两种数据结构,即 multiset
和 priority_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;
}
};
步骤解析:
- 创建一个
multiset
数据结构,将石子堆中的石子数量插入其中。 - 循环执行 k 次操作,每次从
multiset
中取出最大值,执行移除操作,并将新的值插入multiset
。 - 计算剩下石子的总数并返回。
使用 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;
}
};
步骤解析:
- 创建一个
priority_queue
(最大堆)数据结构,将石子堆中的石子数量插入其中,并计算石子总数。 - 循环执行 k 次操作,每次从
priority_queue
中取出最大值,执行移除操作,并将新的值插入priority_queue
。 - 计算剩下石子的总数并返回。
两种数据结构的解析
priority_queue
数据结构解析
priority_queue
是一个优先队列,基于堆结构实现。在 C++ 中,默认情况下,priority_queue
是最大堆,即堆顶元素是队列中的最大元素。在本问题中,我们使用 priority_queue
来维护石子堆的最大值,以便能够高效地执行移除操作。
步骤解析:
- 初始化: 创建一个
priority_queue
对象,将石子堆中的石子数量插入其中,同时计算石子总数。 - 循环操作: 执行 k 次操作,每次从
priority_queue
中取出最大值,即当前堆的最大石子数量。 - 移除操作: 计算需要移除的石子数量(即当前最大值的一半),并更新石子总数。
- 更新队列: 将更新后的石子数量插入
priority_queue
中,保持队列的最大堆性质。
通过这样的操作,我们可以高效地找到并处理每次操作的目标石子堆,从而得到剩下石子的最小总数。
multiset
数据结构解析
multiset
是 C++ 标准模板库中的关联容器,是一个有序集合,允许元素重复。在本问题中,我们使用 multiset
来实现一个有序的石子堆,以便能够方便地找到并处理最大值。
步骤解析:
- 初始化: 创建一个
multiset
对象,将石子堆中的石子数量插入其中,形成有序集合。 - 循环操作: 执行 k 次操作,每次从
multiset
中取出最大值,即当前堆的最大石子数量。 - 移除操作: 计算需要移除的石子数量(即当前最大值的一半),并更新石子总数。
- 更新集合: 移除当前最大值,将更新后的石子数量插入
multiset
中,保持有序性。
通过这样的操作,我们可以在 multiset
中高效地找到并处理每次操作的目标石子堆,得到剩下石子的最小总数。