当前位置:首页 > 提高组 > 2004年NOIP提高组合并果子(洛谷P1090)题解:优先队列与贪心算法的完美应用

2004年NOIP提高组合并果子(洛谷P1090)题解:优先队列与贪心算法的完美应用

4天前

2004年NOIP提高组合并果子(洛谷P1090)题解:优先队列与贪心算法的完美应用 贪心策略 优先队列 NOIP提高组 NOIP题解 洛谷题解 C++ 第1张

一、题目解读

合并果子(洛谷P1090)是2004年NOIP提高组的一道经典题目,原题链接:https://www.luogu.com.cn/problem/P1090。题目描述了一个果园场景:需要将N堆果子合并成一堆,每次合并消耗体力为两堆果子的重量之和,目标是通过设计合并顺序,使总消耗体力最小。数据范围要求1≤N≤10000,每堆果子重量1≤ai≤20000,需输出最小体力值。

二、解题思路

核心思路为贪心算法:每次优先合并当前最小的两堆果子,以降低后续合并的消耗。实现关键在于利用**优先队列最小堆)**自动维护果子堆的重量排序,确保每次取出的两堆果子重量最小,从而保证整体消耗最优。该策略符合哈夫曼(最优二叉树)的构建思想,即权值小的节点优先靠近叶节点,减少路径长度。

三、解题步骤

1. 输入处理:读取果子种类数N及每堆重量,存入优先队列q(小顶堆)。

2. 合并过程:

○ 循环直至队列仅剩一堆:

    弹出队首两堆果子a、b,计算合并消耗sum += a + b;

    将新堆a+b重新入队q,维持堆有序性。

3. 输出结果:最终sum即为最小体力消耗值。

四、代码与注释

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

int main() {  
    int n, sum = 0;  
    cin >> n; // 输入果子种类数N  

    // 创建小顶堆优先队列,自动维护元素升序  
    priority_queue<int, vector<int>, greater<int>> q;  

    for(int i = 0; i < n; ++i) {  
        int weight;  
        cin >> weight; // 输入每堆果子重量  
        q.push(weight); // 初始化堆  
    }  

    while(q.size() > 1) { // 循环至仅剩一堆  
        int a = q.top(); q.pop(); // 取出最小两堆果子  
        int b = q.top(); q.pop();  
        sum += a + b; // 累加当前合并消耗  
        q.push(a + b); // 新堆入列  
    }  

    cout << sum << endl; // 输出最小体力值  
    return 0;  
}

注释解析:

● 优先队列q通过greater<int>参数指定为小顶堆,确保q.top()始终返回当前最小重量堆。

● 循环条件q.size() > 1保证合并次数为N-1,符合题目要求。

● sum变量累计每次合并的消耗,最终结果即为最优解。

五、总结

本解法通过贪心策略与优先队列的结合,将复杂度为O(NlogN)的合并问题高效解决。关键在于“局部最优(每次最小合并)导致全局最优”的证明,体现了贪心算法的核心思想。实际应用中,该思路可扩展至类似资源合并优化场景,如哈夫曼编码树的构建。代码简洁且符合竞赛要求,是学习优先队列与贪心算法的经典案例。



原创内容 转载请注明出处

分享给朋友:

相关文章

力扣1137题:动态规划解泰波那契数 高效求解第N项的秘密

力扣1137题:动态规划解泰波那契数 高效求解第N项的秘密

一:重新解读题目泰波那契数列是一个充满数学趣味的递推序列:从第3项开始,每个数均为前三个数的和(即Tₙ₊₃ = Tₙ + Tₙ₊₁ + Tₙ₊₂)。当给定整数n时,需要高效计算出第n项的值。面对此类递...

IOI 1994 洛谷1216:如何用O(1)空间解决数字三角形问题?附代码实现

IOI 1994 洛谷1216:如何用O(1)空间解决数字三角形问题?附代码实现

题目重解:数字三角形是一个经典的动态规划问题,给定一个由数字组成的三角形结构,从顶部出发,每次可以移动到下方相邻的数字,最终到达底部。我们需要找到一条路径,使得路径上经过的数字总和最大。这个问题可以很...

力扣145:递归之美 轻松掌握二叉树后序遍历

力扣145:递归之美 轻松掌握二叉树后序遍历

题目解读二叉树的后序遍历是一种基础且重要的树遍历方式,其遍历顺序为:先递归地后序遍历左子树,然后递归地后序遍历右子树,最后访问根节点。这种遍历方式特别适合需要先处理子节点再处理父节点的场景,如内存释放...

手搓顺序表类代码注释与详解:从零实现动态数组(新手教程)

一、简介和特点顺序表(Sequential List)是数据结构中基础的一种线性表,其特点是将数据元素存储在连续的内存空间中。通过数组实现,支持随机访问(即通过索引直接访问元素),适用于频繁随机读取的...

牛客14496题解:括号最大深度问题(栈思想与代码优化)

牛客14496题解:括号最大深度问题(栈思想与代码优化)

一、题目解读牛客14496题要求计算给定括号字符串中的最大深度。例如,对于字符串 "(()())",最大深度为2。题目考察对括号嵌套结构的理解,以及如何通过编程找到最深嵌套层次。二...

力扣3112题解法:带时间限制的最短路径问题解析(C++代码)

力扣3112题解法:带时间限制的最短路径问题解析(C++代码)

一、题目解读力扣3112题要求解决带时间限制的最短路径问题:给定一个有向图,节点具有消失时间,需计算从起点到各节点的最短路径,且路径总时间不能超过节点的消失时间。题目难点在于需在传统最短路径算法(如D...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。