点击查看代码
/*
有n堆果子,每堆果子的质量已知,现在需要把这些果子合并成一堆,但是每次只能把两堆果子合并到一起,同时会消耗与两堆果子质量之和等值的体力。
显然,在进行n-1轮后,就只剩一堆了。为了尽可能节省体力,请设计出合并的次序方案,使得耗费的体力最少,并给出消耗的体力值
输入样例:
5
1 2 2 3 6
输出样例:
30
题目分析:
问题等价于已知n个数,将这n个数作为一棵树的n个叶子结点的权值,求出带权路径长度最小的树,也就是求哈夫曼树
样例解释:
哈夫曼树构建思想:反复选择两个最小的结点进行合并,直到只剩一个结点时结束
第1轮:选择最小的1和2合并,获得3。此时结点有:2 3 3 6
第2轮:选择最小的2和3合并,获得5。此时结点有:3 5 6
第3轮:选择最小的3和5合并,获得8。此时结点有:6 8
第4轮:选择最小的6和8合并,获得14,只剩一个元素,合并结束。
哈夫曼树构建完成,可以画出这棵树,计数得到这棵树的带权路径长度为30
*/
/*
题目分析:
1、哈夫曼树可以使用优先队列实现,初始时将所有果堆质量压入优先队列中
2、每轮从优先队列的队首取出两个最小的数,将它们相加合并,然后重新压入队列参与后续的合并
3、设置int ans记录每轮相加的结果,当算法结束时ans就记录了消耗的最小体力
4、当优先队列中只剩一个数时,结束算法
注意:优先队列默认的优先级是按递减排序,而哈夫曼树构建要取两个最小的数,因此要更改优先级按递增排序
*/
#include<cstdio>
#include<queue>
using namespace std;
#pragma warning(disable:4996)
/*优先队列q
第1个参数:存储元素类型
第2个参数:底层数据结构堆的容器类型,使用vector存储堆,类型与第1个参数一样
第3个参数:优先级设置,greater<typename>是元素值小的优先级高,按递增排序
默认不填则使用less<typename>是元素值大的优先级高,按递减排序 */
priority_queue<int, vector<int>, greater<int>> q;
int main() {
//n个结点,temp记录每个结点的权值,x和y记录两个最小权值的结点,ans记录结果
int n, temp, x, y, ans = 0;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &temp);
q.push(temp); //初始时将每个结点的权值压入优先队列q中
}
while (q.size() > 1) { //当优先队列只剩一个数时结束算法
x = q.top(); //将优先队列两个最小权值的结点存入x和y中
q.pop(); //队首元素出队后要手动删除
y = q.top();
q.pop();
q.push(x + y); //将两个最小权值的结点相加合并后重新压入优先队列q中
ans += x + y; //ans累计求和
}
printf("%d\n", ans); //输出消耗的最小体力
return 0;
}