[荷马史诗] — k叉哈夫曼树

题目背景

    追逐影子的人,自己就是影子 ——荷马

题目描述

  

输入格式

  

输出格式

  

输入输出样例

【说明/提示】

【数据规模与约定】

  


 

题意分析

  依据题意,就是要求构造一个K进制的赫夫曼编码。

  我们需要求的是树的WPL和该赫夫曼树的高度。所以在struct里面不仅要记录结点的w,还要记录深度h,并以他们作为小根堆的关键字。

  对于k叉赫夫曼树的求解,直观的想法是在贪心的基础上,改为每次从堆中去除最小的k个权值合并。我们便可以按照赫夫曼树的构造方式,将当前最小的K个节点合并为1个父节点(显然,可以使用优先队列(二叉堆)进行维护),直至只有一个父节点。

  注意一个细节,如果在执行最后一次循环时,堆的大小在(2~k-1)之间(不足以取出k个),那么整个赫夫曼树的根的子节点个数就小于k,也就表明了最靠近根节点的位置反而没有被排满,这显然不是最优解 。因此,我们应该在执行上述贪心算法之前,补加一些额外的权值为0的叶子节点,使叶子节点的个树满足(n-1)%(k-1)=0。

  • 如果k=2,构建传统的二叉赫夫曼树。堆维护即可。
  • 如果k>2,当(n-1)%(k-1)!=0的时候会出现最后一次合并的结点数少于k个,需要增加空节点。
 1 // K阶的赫夫曼树
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<queue>
 6 #include<algorithm>
 7 //#define ll long long
 8 using namespace std;
 9 
10 //定义结点结构体
11 struct node {
12     int w,h;  //w为权重,h为高度
13     node() {w = 0, h=0;}  
14     node(int w, int h): w(w), h(h) {}
15     bool operator <(const node &a)const{  //重载运算符,使w小的结点先出队列
16         return a.w == w ? h>a.h : w>a.w;  //优先考虑权值,其次考虑高度
17     } 
18 };
19 
20 int ans;
21 
22 priority_queue<node>q;  //类型为node的优先队列   
23 
24 int main() {
25     int n,k;
26     cin>>n>>k;
27     for(int i = 1; i <= n; i++) {
28         int w;
29         cin>>w;
30         q.push(node(w,1));   //默认h=11,这里进行强制类型转换
31     }
32     while((q.size()-1) % (k-1) !=0 ) {   //需要添加空节点的情况
33         q.push(node(0,1));  //空节点默认w=0,h=1
34     }
35     while(q.size()>=k) {
36         int h = -1;
37         int w = 0;
38         for(int i = 1; i <= k; ++i) {   //每次选出最小的k个结点
39             node t = q.top();
40             q.pop();
41             h = max(h,t.h);
42             w += t.w;
43         }
44         ans += w;
45         q.push(node(w, h + 1));
46     }
47 
48     cout<<ans<<endl;
49     cout<<q.top().h-1<<endl;
50 
51     system("pause");
52     return 0;
53 }

 

 

posted @ 2021-10-08 22:22  赶紧学习  阅读(331)  评论(0编辑  收藏  举报