Huffman树学习笔记
H u f f m a n Huffman Huffman树用于解决这一类问题:构造一个 n n n个叶子节点的 k k k叉树,其中第 i i i个叶子节点带有权值 w i w_i wi,要求最小化 ∑ w i ∗ l i \sum w_i*l_i ∑wi∗li,其中 l i l_i li表示第 i i i个叶子节点到根节点的距离。
很显然的一个贪心做法:
为了最小化 ∑ w i ∗ l i \sum w_i*l_i ∑wi∗li,应该让权值大的叶子节点的深度尽量小。
当 k = 2 k=2 k=2时,我们可以用如下方法构造二叉 H u f f m a n Huffman Huffman树:
1、建立一个小根堆,插入这 n n n个叶子节点的权值
2、从堆中取出最小的两个权值 w 1 w_1 w1和 w 2 w_2 w2,令 a n s + = w 1 + w 2 ans+=w_1+w_2 ans+=w1+w2
3、建立一个权值为 w 1 + w 2 w_1+w_2 w1+w2的树节点 p p p,使 p p p成为两个叶子结点的父节点,并在堆中插入 p p p
最后 a n s ans ans就是 ∑ w i ∗ l i \sum w_i*l_i ∑wi∗li的最小值
我们将问题拓展,对于 k k k叉 H u f f m a n Huffman Huffman树,我们依样画葫芦可以构造出这样的算法:
1、建立一个小根堆,插入这 n n n个叶子节点的权值
2、从堆中取出最小的 k k k个权值 w 1 , w 2 … … w k w_1,w_2……w_k w1,w2……wk,令 a n s + = ∑ i = 1 k w i ans+=\sum_{i=1}^{k}w_i ans+=∑i=1kwi
3、建立一个权值为 ∑ i = 1 k w i \sum_{i=1}^{k}w_i ∑i=1kwi的树节点 p p p,使 p p p成为 k k k个叶子结点的父节点,并在堆中插入 p p p
但是,这样做下去是无法保证达到最优解的。
因为在最后一轮执行的时候,无法保证一共有 k k k个节点刚好合并成一个节点,也就是说最后的根节点不一定有 k − 1 k-1 k−1个儿子。这样答案显然不是最优的,因为我们可以在底下随便那一个节点作为根节点的儿子,这样也可以让答案最优。
所以,为了让答案最优,我们应该在执行上述贪心算法之前,补加一些额外的权值为0的节点,是叶子结点总数n使得 ( n − 1 ) % ( k − 1 ) = 0 (n-1)\%(k-1)=0 (n−1)%(k−1)=0,意思是,我们让节点不足的情况放在最底层而不是最高层,这样做出来的做法显然是最优的。
荷马史诗
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cMiQSNmk-1607157358975)(C:\Users\Stuedent\AppData\Roaming\Typora\typora-user-images\image-20201029102917160.png)]
每个单词的数量就是每个节点的权值。
按照上述模板做一个 k k k叉 H u f f m a n Huffman Huffman的模板即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+10;
#define gc getchar()
int n,k;
int ans = 0;
typedef pair < int , int > pii;
priority_queue < pii , vector < pii > , greater < pii > > q;
int read(){
int s = 0 , f = 0; char ch = gc;
while (ch < '0' || ch > '9') f|=ch=='-' , ch = gc;
while (ch >= '0' && ch <= '9') s = s*10+ch-48 , ch = gc;
return f?-s:s;
}
signed main(){
n = read() , k = read();
for (int i = 1,x; i <= n; i++) x = read() , q.push({x,1});
while ((n-1)%(k-1)!=0) ++n,q.push({0,1});
while (q.size()>=k){
int w = 0 , h = 0;
for (int i = 1; i <= k; i++)
w+=q.top().first , h = max(q.top().second,h),q.pop();
q.push({w,h+1}); ans+=w;
}
printf("%lld\n%lld",ans,q.top().second-1);
return 0;
}