洛谷-P1090 [NOIP 2004 提高组] 合并果子 题解

题目链接:题解:合并果子(哈夫曼编码思想)

题目大意

有 n 堆果子,每堆有一定数量的果子。每次可以合并任意两堆,消耗的体力等于这两堆果子数量之和。

目标是通过 n-1 次合并将所有果子合并成一堆,使得总消耗体力最小

解题思路

问题本质

这是一道经典的 哈夫曼树(Huffman Tree) 构造问题。

  • 每次合并两堆果子,相当于在哈夫曼树中合并两个叶子节点;
  • 合并代价 = 两堆重量之和;
  • 总代价 = 所有内部节点的权值之和;
  • 要使总代价最小,应总是优先合并当前最小的两堆

贪心策略:每次从所有堆中选出最小的两个进行合并,这样可以让小的数尽可能少地参与后续累加,从而最小化总代价。

数据结构选择

为了高效地获取和维护当前最小的两个元素,使用 最小堆(优先队列)

  • 初始时将所有果子堆数量加入最小堆;
  • 每次取出堆顶两个最小值,合并后将新堆放回堆中;
  • 累加每次合并的代价,直到只剩一堆。

时间复杂度为 O(n log n),满足 n <= 10^4 的要求。

代码解析

#include<iostream>
#include<queue>
#include<vector>
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    // 定义最小堆
    priority_queue<int, vector<int>, greater<int>> Q;
    
    int n, ans = 0;
    cin >> n;
    int val;
    
    // 读入每堆果子数量,加入堆
    for (int i = 0; i < n; i++) {
        cin >> val;
        Q.push(val);
    }
    
    // 不断合并最小的两堆
    while (Q.size() > 1) {
        int sum = 0;
        sum += Q.top(); Q.pop();   // 取出最小
        sum += Q.top(); Q.pop();   // 取出次小
        ans += sum;                // 累加体力消耗
        Q.push(sum);               // 将新堆放回
    }
    
    cout << ans;
    return 0;
}

关键点说明

  • priority_queue<int, vector<int>, greater<int>>:定义最小堆;
  • 每次合并后新堆的重量为 sum,必须重新入堆,参与后续合并;
  • 总共合并 n-1 次,循环条件为 Q.size() > 1
  • 最终输出 ans 即为最小体力耗费值。

复杂度分析

  • 时间复杂度:O(n log n)每次 pop 和 push 操作耗时 O(log n),共进行 n-1 次合并,每次操作 2 次 pop 和 1 次 push。
  • 空间复杂度:O(n)堆中最多存储 n 个元素。

常见误区

  1. 错误贪心:有人可能想先合并大的堆,但这样会导致小堆被多次累加,总代价反而更大。
  2. 使用数组排序模拟:每次排序再取前两个,复杂度 O(n^2 *log n),会超时。
  3. 忽略数据范围:虽然题目保证结果小于 2^31,但中间变量仍建议用 int(因 n <=10^4, ai <=2*10^4,最大总和约 2* 10^8,在 int 范围内)。

总结

本题是哈夫曼编码的经典应用,核心在于理解“越早合并的果子,其重量会被累加得越多”,因此应让重量7小的果子尽早合并,以减少它们在后续合并中的重复计算。使用最小堆实现贪心策略,简洁高效,是解决此类合并优化问题的标准方法。

全部评论

相关推荐

11-29 00:55
门头沟学院 Java
区域赛银,邀请赛金,打算十二月打下Java基础、背点八股、写个外卖后去投福建小厂的寒假实习,简历应该怎么写呢?以及福州/和厦门有推荐的小厂吗?
牛客53210502...:简历一页:把区域银,邀请赛金标粗,其他的奖除非凑一页否则没有必要写。或者多页:每个站一行这样都列出来。项目经历看看牛客其他人是怎么写的,写的不好呢。简历打磨好按部就班没问题的
点赞 评论 收藏
分享
11-23 21:15
门头沟学院 Java
实习&nbsp;1.threadpooltaskexecutor是什么作用2.forkpooljoin&nbsp;起什么作用?底层窃取算法怎么实现的?3.为什么更适合&nbsp;cpu&nbsp;密集型?那你如何防止他创建大量线程的(我答换了使用另一个自定义线程池(核心线程数和最大线程数设置成一样)?那核心线程和最大线程设置成一样是什么效果?4.你用countdownlatch&nbsp;作用是啥?await&nbsp;是如何做到阻塞在那的?底层原理?聊一聊&nbsp;AQS?八股1.聊一聊泛型?实际有用过吗?用的多的有哪些场景?2.反射主要用在什么场景?你的理解中为什么需要反射这种机制?3.有了解过动态语言和静态语言么(这里蒙了,然后跟我解释是编译型和解释型语言的意思),java&nbsp;属于哪种?为什么说是半编译半解释?4.讲讲&nbsp;AOP?5.java&nbsp;生成代理有哪几种方式?讲下静态代理和动态代理,动态代理动态在哪里?生成动态代理的方式有哪些?6.集合了解哪些?CopyOnWriteArrayList是怎么实现并发安全的?CopyOnWriteArrayList如果只是读写分离不是会有数据不一致的问题么,有进一步了解么?7.HashMap&nbsp;和&nbsp;ConcurrentHashMap&nbsp;区别?JDK8&nbsp;ConcurrentHashMap锁的粒度这么小的话不会有额外的开销吗?它用的是什么锁?8.Syncronized&nbsp;锁的原理?9.了解哪些&nbsp;GC&nbsp;算法?G1&nbsp;从哪个版本开始有的?jdk8默认垃圾回收器是什么?10.JVM&nbsp;内存分哪几个区域?11.双亲委派模型讲讲?为什么要避免重复加载?有了解要破坏双亲委派的场景吗?破坏双亲委派模型要重写的类叫什么?12.数据库&nbsp;MVCC&nbsp;机制讲下?它是怎么做到让一些事物不可见的?它是怎么知道前面有一串事物&nbsp;id&nbsp;的?13.undolog&nbsp;作用?14.spring&nbsp;里面怎么注入一个&nbsp;bean?15.@Resource&nbsp;和@Autowired&nbsp;区别?16.Spring&nbsp;默认事务传播级别?17.事物注解失效的场景?为什么自调用会导致事物失效?如果代码就写成这样了,自调用导致事物失效了,怎么办?18.布隆过滤器讲下?弊端?
查看22道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务