哈夫曼树 荷马史诗-洛谷

这道题目做了五个多小时,主要还是不了解哈夫曼树的一些细节问题,自己做个总结吧。

题目链接: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个节点(也就是利用空节点将其余的节点挤到更优的位置上)。

二:一些概念

  1. 结点带权路径长度:L*W(W是这个结点的权值,L是这个结点的深度,根节点的深度是0)。
  2. 树的带权路径长度:所有叶子结点的结点带去按路径长度之和。

三:思想:

 自底向上,每次选择最小的两个结点,合成一个结点。

四:特点:

  1. 不存在度为1的结点
  2. n个结点的哈夫曼树总共需要结点2n-1,所以对于二进制的编码,开的空间最小是2n-1。
  3. 顺序存储,存在结构体里面挺好。
#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;
}

猜你喜欢

转载自blog.csdn.net/qq_41755258/article/details/84326681
今日推荐