在文件合并、外排序等场景中,经常需要将多个已有序的子文件合并为一个有序文件。每次合并两个长度为
题目
给定
输入格式
- 第一行一个整数
; - 第二行
个整数,表示各有序表的长度。
输出格式
- 输出一个整数,表示最小合并总代价。
样例
样例输入
input
3
1 2 3样例输出
output
9样例解释
三段长度分别为
贪心策略(每次合并最短的两段)的执行过程:
- 取出最短的两段
和 ,合并为新段 ,代价 ,剩余 ; - 取出
和 ,合并为新段 ,代价 ; - 只剩一段,结束。总代价
。
这个合并过程可以画成一棵树:每次合并对应一个内部节点,其权值为两段之和,总代价恰好等于所有内部节点权值之和。可以看到,它与哈夫曼树的构建过程完全一致——这正是本题与哈夫曼编码同源的原因。
题解
点击查看题解
核心思路
最优合并问题与哈夫曼编码本质相同,都是构建哈夫曼树求最小带权路径长度。
贪心策略:每次选取长度最小的两个有序表合并。可用最小堆维护当前所有表的长度,每次取出两个最小值合并,将新长度放回堆中。
复杂度分析
- 时间复杂度:
; - 空间复杂度:
。
点击查看参考代码
cpp
#include <iostream>
#include <queue>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
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);
}
long long total = 0;
while (pq.size() > 1) {
long long a = pq.top(); pq.pop();
long long b = pq.top(); pq.pop();
long long c = a + b;
total += c;
pq.push(c);
}
cout << total << '\n';
return 0;
}本地运行与提交
powershell
pnpm lab:run -- labs/chapter-05/exercise/E-05-16-optimal-merge