哈夫曼树学习笔记及1070 结绳 (25分)练习

本文介绍了哈夫曼树的概念,包括其定义、构建方法,以及如何利用哈夫曼树解决经典问题——合并果子问题。通过优先队列实现合并策略,确保消耗最少体力。此外,还探讨了哈夫曼树在1070 结绳问题中的应用,同样利用哈夫曼思想,先串联最短的绳子以减少操作次数。
摘要由CSDN通过智能技术生成

1.哈夫曼树的定义:

已知n个数,寻找一棵树使得树的所有叶子结点的权值恰好为这n个数,并且使这棵树的带权路径长度(每个叶子结点的权值乘以其路径长度的结果之和)最小。(又称为最优二叉树)
注:
对于同一组叶子结点来说,哈夫曼树可以是不唯一的,但最小带权路径长度一定是唯一的。

2.构建方法:

在构建树的过程中,在已有结点中反复选择两个最小元素,合并,直至只剩下一个元素(根结点)。

3.经典问题–合并果子问题

有n堆果子,每堆果子的质量已知,现在需要把这些果子合并为一堆,但是每次只能把两堆果子合并到一起,同时会消耗与两堆果子质量之和等值的体力。为了尽可能节省体力,请设计出合并的次序方案。

思考:
最先合并的果子在整个过程中消耗的体力是最多的,因此应该挑选质量最小的果子最先合并。
则将每堆果子都看作叶节点,非叶子结点为合并过程中新生成的结点,可用哈夫曼思想来解决

首先掌握优先队列的使用方法:
优先队列底层是用堆来进行实现的,每次向优先队列中push新元素后,其底层的堆都会及时调整结构使队首元素始终优先级最高。
(关于堆的理解可参考上一篇博客)
priority_queue<int,vector< int >,less< int> >q
上面的声明代表队首元素始终是优先队列内元素最大的那一个。其中第一个参数代表队列中数据类型,第二个参数代表承载底层数据结构堆的容器,(假如优先队列中的数据类型为double,则第二个参数就位vector),less 代表数字大的优先级大。
同理,若要求队列内元素最小的那个 优先级最高,则声明为:
priority_queue<int,vector< int >,greater< int> >q

则求上述问题中消耗最小体力值得代码为:

priority_queue<long long, vector<long long>, greater<long long> >q;

int main()
{
	int n;
	long long temp, x, y, ans = 0;
	scanf("%d", &n); //共有多少堆果子
	for (int i = 0; i < n; i++)
	{
		scanf("%lld", &temp);
		q.push(temp);
	}
	while (q.size() > 1)
	{
		x= q.top();
		q.pop();
		y = q.top();
		q.pop();
		q.push(x + y);  //找出当前结点最小的两个结点,求和后入队
		ans += x + y;  
	}
	printf("%lld", ans);
}

4.练习 1070 结绳 (25分)

思考:最先串联的绳子在整个过程中被减半的次数最多,因此应该让最短的绳子最先串联。也即给定的N段绳子为叶子结点,串联生成的绳子为非叶子结点,可用哈夫曼树来解决,
在这里插入图片描述
在这里插入图片描述

int main()
{
	//绳子长度由短到长排序
	priority_queue<double,vector<double>,greater<int> > q;

	int n;
	double len;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%lf", &len);
		q.push(len);
	}
	while (q.size() > 1)
	{
		double l1 = q.top();
		q.pop();
		double l2 = q.top();
		q.pop();
		q.push((l1 / 2) + (l2 / 2));
	}
	double res = q.top();
	cout << floor(res);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值