原题链接:https://www.luogu.com.cn/problem/P1090

题目概述

这道题目描述了一个果园合并果子的问题,要求我们找到合并所有果堆的最小体力消耗方案。这是一个典型的贪心算法问题,也是优先队列(堆)数据结构的经典应用。

解题思路

要最小化合并果子的体力消耗,我们应该每次都合并当前最小的两堆果子。这种策略可以确保较大的果堆尽可能少地被重复合并。具体步骤如下:

  1. 将所有果堆的重量放入最小堆(优先队列)中

  2. 每次取出两个最小的果堆进行合并

  3. 将合并后的新果堆重量重新放入堆中

  4. 累加每次合并的体力消耗

  5. 重复上述过程直到只剩一堆果子

参考程序

#include<bits/stdc++.h>
#define i64 long long  // 定义长整型别名,防止大数溢出
#define endl "\n"
using namespace std;

// 定义最小优先队列,存储果子堆的重量
priority_queue<int, vector<int>, greater<int>> pq;
int n, x;  // n-果子种类数,x-临时变量

int main() {
    cin >> n;  // 输入果子种类数
    
    // 读取每种果子的数量并存入优先队列
    for(int i = 1; i <= n; i++) {
        cin >> x;
        pq.push(x);  // 将果子数量加入最小堆
    }
    
    i64 sum = 0;  // 总体力消耗,使用长整型防止溢出
    
    // 当堆中还有多于一堆果子时继续合并
    while(pq.size() > 1) {
        // 取出当前最小的两堆果子
        int a = pq.top(); pq.pop();
        int b = pq.top(); pq.pop();
        
        // 累加本次合并的体力消耗
        sum += (a + b);
        
        // 将合并后的新堆重新放入优先队列
        pq.push(a + b);
    }
    
    // 输出最小体力消耗值
    cout << sum;
    
    return 0;
}

代码解析

  1. 数据结构选择:使用priority_queue实现最小堆,确保每次都能快速获取最小的两个元素。

  2. 输入处理:首先读取果子种类数n,然后读取n个果堆的重量并存入优先队列。

  3. 合并过程

    • 使用while循环,直到堆中只剩一堆果子

    • 每次取出两个最小的果堆a和b

    • 将a+b累加到总消耗sum中

    • 将合并后的新堆(a+b)重新放入堆中

  4. 输出结果:最终输出累计的体力消耗值sum。

复杂度分析

  • 时间复杂度:O(n log n),每个元素插入和删除堆的操作都是O(log n),共进行O(n)次操作

  • 空间复杂度:O(n),需要存储所有果堆的重量

示例验证

以题目中的样例为例:
输入:3种果子,重量分别为1、2、9

  1. 初始堆:[1, 2, 9]

  2. 第一次合并:1+2=3,消耗3 → 堆:[3, 9],sum=3

  3. 第二次合并:3+9=12,消耗12 → 堆:[12],sum=15

  4. 输出结果:15