合并果子

题目描述

在一个果园里,达达已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。
达达决定把所有的果子合成一堆。
每一次合并,达达可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。
可以看出,所有的果子经过 n−1次合并之后,就只剩下一堆了。
达达在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以达达在合并果子时要尽可能地节省体力。
假定每个果子重量都为 1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使达达耗费的体力最少,并输出这个最小的体力耗费值。
例如有 3种果子,数目依次为 1,2,9。
可以先将 1、2堆合并,新堆数目为 3,耗费体力为 3。
接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12,耗费体力为 12
所以达达总共耗费体力=3+12=15。
可以证明 15为最小的体力耗费值。

输入格式

输入包括两行,第一行是一个整数 n,表示果子的种类数。

第二行包含 n个整数,用空格分隔,第 i个整数 ai是第 i种果子的数目。

输出格式

输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。

输入数据保证这个值小于 2^31

数据范围

1≤n≤10000
1≤ai≤20000

输入示例

1
2
3
1 2 9

输出示例

1
15

思路及注意事项

略:
利用哈夫曼树进行解题;
在哈夫曼树中,位于树的底部的是最小的数,也就是最原始的那几堆水果
通过堆排序与堆运算,完成计算
详细:
声明优先队列:声明一个名为heap的优先队列,元素类型为int,底层容器为vector,比较函数为greater,表示堆顶元素是最大的。

输入整数并加入堆:使用for循环,从标准输入读取n个整数,并将它们加入到堆heap中。

处理堆中的元素:使用while循环,当堆中元素数量大于1时执行以下操作:

1.取出堆顶元素a,并将其从堆中删除。
2.取出堆顶元素b,并将其从堆中删除。
3.将a和b的和加入到结果res中。
4.将a和b的和加入到堆中。循环结束后,堆中只剩下一个元素。

哈夫曼树: 最小的数在树的最下边,
具有最优子结构:按照每一步最好的方式走,可以达到最值
对于有极值的函数来说,不具有最优子结构,如小偷抢钱那一道题;也就是说,每次都选最好的,能不能达到全局最优。具有最优子结构就行,不具有不行

##代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
int main(){
int n;
//输入果子的堆数
cin>>n;
//定义了一个名为heap的优先队列(priority queue)。
//优先队列是一种数据结构,它类似于常规的队列,但每个元素都有一个优先级。
//元素的出队顺序是根据它们的优先级决定的,优先级最高的元素总是最先出队。
//该队列的元素类型是int(整数),底层容器使用vector<int>(整数向量),
//并且元素的比较是按照降序进行的(greater<int>)。
priority_queue<int,vector<int>,greater<int> >heap;
//循环输入每堆的个数,并把它们放进队列里
for(int i=0;i<n;i++){
int x;
cin>>x;
heap.push(x);
}
//定义消耗的体力数
int res=0;
//利用循环将队列最顶层的(最小的)元素取出,并删去
//将去除的两项相加,直到队列里只剩下一组,循环结束,得到最小的体力消耗值
while(heap.size()>1){
int a=heap.top();heap.pop();
int b=heap.top();heap.pop();
res+=a+b;
heap.push(a+b);
}
cout<<res<<endl;
return 0;

}