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

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;
}

功能分析

  1. 输入处理 :读取果子种类数 n 和每种的数目,依次插入小根堆。
  2. 合并过程:每次从堆顶取出最小的两堆(堆自动维护最小堆性质),合并后得到的新堆大小等于两者之和,累加到答案,并将新堆放回堆中。
  3. 循环终止:当堆中只剩一堆时结束,此时答案即为最小体力耗费值。
  4. 正确性:哈夫曼算法贪心性质保证每次合并最小的两堆能得到全局最优解,优先队列模拟了该过程。
  5. 边界情况 :若 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;
}
相关推荐
apocelipes19 小时前
常用编程语言和库的正则表达式性能对比
c语言·c++·python·性能优化·golang·开发工具和环境
HjhIron20 小时前
面试常客:字符串算法从入门到进阶
算法·面试
吴佳浩1 天前
DeepSeek DSpark:Confidence-Scheduled Speculative Decoding 技术解析
人工智能·算法·deepseek
触底反弹1 天前
🧠 搞懂 Token,才算真正入门大模型——从分词原理到 Embedding 语义实战
javascript·人工智能·算法
vivo互联网技术1 天前
ICLR 2026 | 基于后验采样的图像恢复方法LearnIR:人脸去阴影、去雾
人工智能·算法·aigc
浮生望1 天前
JS字符串与回文算法:从包装类到双指针的面试进阶之路
javascript·算法
黄敬峰1 天前
面试必刷:从JS底层包装类到双指针,彻底搞懂字符串与回文算法
算法
地平线开发者2 天前
J6B vio scenario sample
算法
BothSavage2 天前
Trae远程开发中DeepSeek自定义模型4054错误的排查与修复
算法