AcWing 148. 合并果子
本节目标
每次合并当前最轻的两堆果子,用小根堆实现最小总代价的 Huffman 式贪心。
这是局部约束与构造中最小代价合并的拓展母题。
题意与约束
每次选择两堆果子合并,代价等于两堆重量之和,合并后成为一堆。求合成一堆的最小总代价。
直接思路与瓶颈
枚举每一步选择会产生大量合并顺序。较重的中间堆若过早生成,会在后续被重复计费,代价通常更大。
贪心模型与算法推导
每次取当前最小的两堆合并,把新堆放回集合。小根堆支持重复地取出两个最小值与插入新值。
正确性依据
两堆最小权重可在某个最优合并树中作为最深的一对兄弟;若更重的叶子位于更深处,与较轻叶子交换不会增大总代价。先合并两最小堆后,剩余问题仍是同一形式,故可递归应用该选择。
样例执行过程
[1,2,9] 先合并 1+2=3,总代价为 3;堆变为 [3,9],再合并得到 12,总代价 15。
代码实现
- C++
- Python
C++17
#include <functional>
#include <queue>
#include <vector>
using namespace std;
long long minMergeCost(vector<int> weights) {
priority_queue<long long, vector<long long>, greater<long long>> heap(weights.begin(), weights.end());
long long answer = 0;
while (heap.size() > 1) {
const long long merged = heap.top(); heap.pop();
const long long second = heap.top(); heap.pop();
answer += merged + second;
heap.push(merged + second);
}
return answer;
}
Python 3
import heapq
def min_merge_cost(weights: list[int]) -> int:
heap = list(weights)
heapq.heapify(heap)
answer = 0
while len(heap) > 1:
merged = heapq.heappop(heap) + heapq.heappop(heap)
answer += merged
heapq.heappush(heap, merged)
return answer
复杂度分析
进行 n-1 次堆操作,时间 O(n log n);堆占用 O(n) 空间。
边界与易错点
- 只有一堆时无需合并,代价为
0。 - 必须是小根堆,不能每轮选择两堆最大的果子。
- 累计代价使用较宽整数类型更稳妥。
模式迁移
最优编码、文件合并和木板切割等“中间结果会再次计费”的问题,常对应每次合并两个最小权重的模型。