普通哈夫曼树是二叉的,每次合并两个节点。本题将其推广到
题目
给定
若某次操作时剩余节点数不足
求最小的总代价。
输入格式
- 第一行两个整数
和 ; - 第二行
个非负整数,表示各叶子节点的权值。
输出格式
- 输出一个整数,表示最小总代价。
样例
样例输入
input
4 3
1 2 3 4样例输出
output
13样例解释
四个叶子权值
正确做法是先补零:满足
- 合并
→ 新节点 ,代价 ; - 合并
(虚拟合并结果)、 (原权值)、 → 新节点 ,代价 ; - 只剩一个节点,结束。总代价
。
虚拟节点让权值小的叶子「少占一层」,这正是带权路径长度最小的关键。
题解
点击查看题解
核心思路
- 若
,则需要补充权值为 的虚拟节点,使得 能被 整除; - 每次从最小堆中取出
个最小权值的节点合并; - 累加合并代价。
补零的原因:在
复杂度分析
- 时间复杂度:
; - 空间复杂度:
。
点击查看参考代码
cpp
#include <iostream>
#include <queue>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n >> k;
priority_queue<long long, vector<long long>, greater<long long>> pq;
for (int i = 0; i < n; ++i) {
long long x; cin >> x;
pq.push(x);
}
// 补充虚拟节点,使 (n-1) % (k-1) == 0
while ((int)pq.size() > 1 && ((int)pq.size() - 1) % (k - 1) != 0) {
pq.push(0);
}
long long total = 0;
while ((int)pq.size() > 1) {
long long sum = 0;
for (int i = 0; i < k && !pq.empty(); ++i) {
sum += pq.top(); pq.pop();
}
total += sum;
pq.push(sum);
}
cout << total << '\n';
return 0;
}本地运行与提交
powershell
pnpm lab:run -- labs/chapter-05/exercise/E-05-17-k-ary-huffman