csp信奥赛C++高频考点专项训练之贪心算法 --【哈夫曼贪心】:合并果子

题目描述
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n − 1 n-1 n−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1 1 1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有 3 3 3 种果子,数目依次为 1 1 1, 2 2 2, 9 9 9。可以先将 1 1 1、 2 2 2 堆合并,新堆数目为 3 3 3,耗费体力为 3 3 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 12 12,耗费体力为 12 12 12。所以多多总共耗费体力 = 3 + 12 = 15 =3+12=15 =3+12=15。可以证明 15 15 15 为最小的体力耗费值。
输入格式
共两行。
第一行是一个整数 n ( 1 ≤ n ≤ 10 4 ) n(1\leq n\leq 10^4) n(1≤n≤104),表示果子的种类数。
第二行包含 n n n 个整数,用空格分隔,第 i i i 个整数 a i ( 1 ≤ a i ≤ 2 × 10 4 ) a_i(1\leq a_i\leq 2\times 10^4) ai(1≤ai≤2×104) 是第 i i i 种果子的数目。
输出格式
一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2 31 2^{31} 231。
输入输出样例 #1
输入 #1
3
1 2 9
输出 #1
15
说明/提示
对于 30 % 30\% 30% 的数据,保证有 n ≤ 10 3 n \le 10^3 n≤103;
对于 50 % 50\% 50% 的数据,保证有 n ≤ 5 × 10 3 n \le 5\times10^3 n≤5×103;
对于全部的数据,保证有 n ≤ 10 4 n \le 10^4 n≤104。
思路分析
这道题要求将若干堆果子合并成一堆,每次合并两堆,消耗的体力等于两堆重量之和,目标是最小化总消耗。这是典型的哈夫曼树问题,最优策略是每次选择当前最小的两堆进行合并。使用小根堆(优先队列)可以高效实现:将所有果子数插入小根堆,循环取出最小的两个,合并后放回堆中,累加消耗,直到堆中只剩一堆。时间复杂度 O(n log n),空间复杂度 O(n)。
代码实现
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n; // 果子种类数
cin >> n;
priority_queue<int, vector<int>, greater<int>> pq; // 小根堆
for (int i = 0; i < n; ++i) {
int a; // 每种果子的数目
cin >> a;
pq.push(a);
}
long long ans = 0; // 最小体力耗费值
while (pq.size() > 1) { // 直到只剩一堆
int x = pq.top(); pq.pop(); // 取最小堆
int y = pq.top(); pq.pop(); // 取次小堆
int s = x + y; // 合并后的新堆大小
ans += s; // 累加消耗
pq.push(s); // 新堆入堆
}
cout << ans << endl;
return 0;
}
功能分析
- 输入处理 :读取果子种类数
n和每种的数目,依次插入小根堆。 - 合并过程:每次从堆顶取出最小的两堆(堆自动维护最小堆性质),合并后得到的新堆大小等于两者之和,累加到答案,并将新堆放回堆中。
- 循环终止:当堆中只剩一堆时结束,此时答案即为最小体力耗费值。
- 正确性:哈夫曼算法贪心性质保证每次合并最小的两堆能得到全局最优解,优先队列模拟了该过程。
- 边界情况 :若
n=1,不进入循环,直接输出 0(无需合并,体力消耗为0),符合题目要求。
各种学习资料,助力大家一站式学习和提升!!!
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"########## 一站式掌握信奥赛知识! ##########";
cout<<"############# 冲刺信奥赛拿奖! #############";
cout<<"###### 课程购买后永久学习,不受限制! ######";
return 0;
}
【秘籍汇总】(完整csp信奥赛C++学习资料):
1、csp/信奥赛C++,完整信奥赛系列课程(永久学习):
https://edu.csdn.net/lecturer/7901 点击跳转

2、CSP信奥赛C++竞赛拿奖视频课:
https://edu.csdn.net/course/detail/40437 点击跳转

https://edu.csdn.net/course/detail/41081 点击跳转

3、csp信奥赛高频考点知识详解及案例实践:
CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转
CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转
信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html 点击跳转
4、csp信奥赛冲刺一等奖有效刷题题解:
CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新): https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转
信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13125089.html 点击跳转
5、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html 点击跳转
· 文末祝福 ·
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"跟着王老师一起学习信奥赛C++";
cout<<" 成就更好的自己! ";
cout<<" csp信奥赛一等奖属于你! ";
return 0;
}