这道题目做了五个多小时,主要还是不了解哈夫曼树的一些细节问题,自己做个总结吧。
题目链接:https://www.luogu.org/problemnew/show/P2168
总结:
一:这道题目的一些收获
1:求最终编码文本的长度,不一定必须让字符组成的编码的长度信息必须存储在叶子结点。可以在建立的时候,定义一个ans=0,在从下向上建立哈夫曼树的时候,不断的相加。比如,有个高度为h的结点,他的结点带权路径长度(不懂定义看二的概念),可以是h个w(权重)相加。这在建树的过程中可以实现。本题目就是采用的这种方式
2:结点的选择:选择权值最小,并且高度较小的。(权值较小的可以理解,那么高度较小呢?题目中说了,求的是最长字符串 si 的最短长度,有两个权值相同的结点,将h更大的结点,留到后面,会让最长字符串的最短长度更小)当时自己就是错在这里,火来参考了大神的代码
3:***添加空结点
当时多进制的时候。因为每次都是将k个节点合并为1个(减少k-1个),一共要将n个节点合并为1个,如果(n-1)%(k-1)!=0 则最后一次合并时不足k个。也就表明了最靠近根节点的位置反而没有被排满,因此我们需要加入k-1-(n-1)%(k-1)个空节点使每次合并都够k个节点(也就是利用空节点将其余的节点挤到更优的位置上)。
二:一些概念
- 结点带权路径长度:L*W(W是这个结点的权值,L是这个结点的深度,根节点的深度是0)。
- 树的带权路径长度:所有叶子结点的结点带去按路径长度之和。
三:思想:
自底向上,每次选择最小的两个结点,合成一个结点。
四:特点:
- 不存在度为1的结点
- n个结点的哈夫曼树总共需要结点2n-1,所以对于二进制的编码,开的空间最小是2n-1。
- 顺序存储,存在结构体里面挺好。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
struct Node{
ll cnt,h;
Node(ll CNT,ll H){
cnt=CNT,h=H;
}
};
bool operator<(const Node a,const Node b){
if(a.cnt!=b.cnt) return a.cnt>b.cnt;
else return a.h>b.h;
}
priority_queue<Node> q;
int main()
{
int n,len,i,j;
cin>>n>>len;
for(i=0;i<n;i++){
ll temp;
scanf("%lld",&temp);
q.push(Node(temp,1));
}
if(0!=((n-1)%(len-1))){
int add=len-1-(n-1)%(len-1);
for(i=0;i<add;i++) q.push(Node(0,1));
n+=add;
}
ll ans=0;
while(q.size()>1)
{
ll sum=0,maxh=0;
for(i=0;i<len;i++){
maxh=max(q.top().h,maxh);
sum+=q.top().cnt;
q.pop();
}
ans+=sum;
q.push(Node(sum,maxh+1));
}
printf("%lld\n%lld",ans,q.top().h-1);
return 0;
}