0. 前言
经典的哈夫曼树模型。
1. 构建Huffman树+贪心
贪心思路:
- 每次选择最小的两个点合并,采用小根堆存储
- 这些石子会构成一颗哈夫曼树
证明:
- 证明1: 权值最小的两个点,在哈夫曼树中的深度一定最深,且可以互为兄弟节点,即可以优先合并,且代价最小
- 反证法,假设最小两点深度不是最深,设点
a、f
是最小的两个点。当交换点b
和f
后,影响到最终结果会从3b+2f+C
变为3f+2b+C
,因为f<b
,所以结果变小了,即交换后整体合并代价变小。即有:合并最小的两个点是局部最优解
- 反证法,假设最小两点深度不是最深,设点
- 证明2: 一定可以通过局部最优解得到全局最优解
a,b
是两个最小的果子,设函数f(x)
表示一棵大小为x
的哈夫曼树的代价。F(x)
表示数大小为x
的最小代价由局部最优解得:f(n) = f(n-1) + a + b
。因为不管是哪一种方案,第一步总是要合并a+b
,因此先不考虑a+b
,只考虑f(n-1)
,于是问题变为求f(n-1)
的最优解,于是可以再调用局部最优解的方案,以此类推直到n=1
。故,证得:可以通过局部最优解的方式得到全局最优解
思路简单,证明困难,这个证明真的蛮抽象的,我感觉自己说都没说明白。
哈夫曼问题见的真的蛮多的,笔试中经常遇见求 WPL
、构建哈夫曼树、求哈夫曼编码等问题…但是一直没总结(懒)…
代码:
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
int main() {
int n;
cin >> n;
priority_queue<int, vector<int>, greater<int>> heap;
while (n --) {
int a;
cin >> a;
heap.push(a);
}
int res = 0;
while (heap.size() > 1) {
int a = heap.top(); heap.pop();
int b = heap.top(); heap.pop();
res += a + b;
heap.push(a + b);
}
cout << res << endl;
return 0;
}