任务描述
本关任务:果园里有N
堆不同重量的果子,现在需要将它们合成一堆,每次只能合并两堆果子,并且消耗两堆果子重量和的能量,请问如何将所有果子合成一堆消耗最少的能量。
相关知识
为了完成本关任务,你需要掌握:1.优先队列,2.求解思路。
优先队列
优先队列priority queue
是一种特殊的队列结构。基础队列的特性是先进先出:在队尾添加新的元素,在队首访问和删除。而优先队列中的元素被赋予优先级的概念:当访问或删除元素时,具有最高优先级的元素最先被操作,也就是说优先队列具有最高级先出first in, largest out
的特性,通常具体采用堆的数据结构来实现。堆是一种比较复杂的数据结构,本实例重点在学习STL
模板中的优先队列,所以不过多介绍堆的具体实现。
优先队列priority_queue
容器的C++
标准头文件为queue
,提供的基础操作及实例如下:
empty
判断优先队列是否为空,若空则返回true
,否则返回false
;size
返回当前优先队列的大小,即优先队列里元素的个数;top
返回优先队列的最高优先级的元素;push
将元素添加到优先队列,STL
中的优先队列会根据元素的优先级自动调整;pop
移除优先队列的队首元素。
#include <iostream> // std::cout
#include <queue> // std::priority_queue
int main (){
std::priority_queue<int> mypq; // 创建一个整型的优先队列,默认数值越大优先级越高
mypq.push(30); // 加入元素
mypq.push(100); // 加入元素
mypq.push(25);
mypq.push(40);
std::cout << "Popping out elements...";
while (!mypq.empty()){ // 判断优先队列是否为空
std::cout << ' ' << mypq.top(); // 获取队首(最高优先级)元素
mypq.pop(); // 移除队首元素
}
std::cout << '\n'; // 输出结果为100 40 30 25
return 0;
}
创建一个优先队列,需要制定数据类型的优先级比较方式,基础的数据类型会有默认的优先级比较方式,例如上面的整型数据默认的比较方式为数值越大优先级越高。
求解思路
显然本问题就是一个典型的优先队列的问题,每次选择重量最小的两堆果子合并,最终消耗的能量是最少的。因为重量大的果子重复搬运比重量小的果子重复搬运会增加额外的能量消耗。
通过创建一个整型的优先队列pque
可以直接求解该问题,需要注意的是优先级为数值越大优先级越高,而本问题需要的是数值越小优先级越高,因此我们可以通过一个巧妙的方式来求解:将所有果子重量乘以-1
转化为负值。具体步骤如下,定义一个整型变量ans
存储能量消耗,初始为0
:
step1
:将所有果子重量加入优先队列pque
;step2
:若队列非空,则获取队首元素存入整型变量q1
并移除队首元素,否则算法结束;step3
:若队列非空,则获取队首元素存入整型变量q2
并移除队首元素,否则算法结束;step4
:合并两堆果子q1
和q2
,并将合并后的重量q1+q2
添加到队列中,更新ans
为ans=ans-q1-q2
;step5
:跳转到step2
。
另外,STL
也提供了通过重载结构体的方式自定义优先级,如下:
// 方式一
priority_queue<int,vector<int>, greater<int> > que1; // x小的优先级高
// 方式二
struct comp{
bool operator()(int x,int y){
return x>y; //重载()的方式,x小的优先级高
}
};
priority_queue<int, vector<int>, comp> que2; // 从小到大的优先队列
// 方式三
struct node {
int x, y;
friend bool operator < (node a, node b) {
return a.x > b.x; //结构体类型的方式,x小的优先级高
}
};
priority_queue<node> que3;
编程要求
本关的编程任务是补全右侧代码片段main
中Begin
至End
中间的代码,具体要求如下:
- 在
main
中,读取数据,基于优先队列的数据结构求解N
堆果子合并的问题,计算出最少能量,并输出结果。
测试说明
平台将自动编译补全后的代码,并生成若干组测试数据,接着根据程序的输出判断程序是否正确。
以下是平台的测试样例:
测试输入: 4
3 12 9 11
预期输出: 70
样例解释: 首先合并3 9两堆果子并形成重量为12的新堆果子,消耗能量12,当前还剩11 12 12三堆果子
接着合并11 12两堆果子并形成重量为23的新堆果子,消耗能量23,当前还剩12 23两堆果子
最后合并余下的两堆果子,消耗能量35
总共消耗能量为12+23+35=70
输入格式: 第一行:果子堆数N
第二行:N堆果子的重量
输出格式: 一行:消耗的最少能量
//
// main.cpp
// step3
//
// Created by ljpc on 2018/9/13.
// Copyright © 2018年 ljpc. All rights reserved.
//
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
int main(int argc, const char * argv[]) {
// 请在这里补充代码,完成本关任务
/********* Begin *********/
int N;
cin >> N;
priority_queue<int, vector<int>, greater<int>> pq; // 创建最小堆
for (int i = 0; i < N; ++i) {
int weight;
cin >> weight;
pq.push(weight);
}
int total_energy = 0;
while (pq.size() > 1) {
int first = pq.top();
pq.pop();
int second = pq.top();
pq.pop();
int new_weight = first + second;
total_energy += new_weight;
pq.push(new_weight);
}
cout << total_energy << endl;
/********* End *********/
return 0;
}