优先队列入门题
合并果子
来源:https://www.luogu.com.cn/problem/P1090
题目描述
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n-1次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。假定每个果子重量都为 1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有 3 种果子,数目依次为 1 ,2 ,9 。可以先将 1,2 堆合并,新堆数目为 3 ,耗费体力为 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 ,耗费体力为 12 。所以多多总共耗费体力 =3+12=15=3+12=15 。可以证明 15 为最小的体力耗费值。
输入格式
共两行。
第一行是一个整数 (1≤n≤10000) ,表示果子的种类数。
第二行包含 n 个整数,用空格分隔,第 i个整数 (1≤ai≤20000) 是第 ii 种果子的数目。
输出格式
一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2^31 。
输入 #1
3
1 2 9
输出 #1
15
说明/提示
对于30%的数据,保证有n≤1000:
对于50%的数据,保证有n≤5000;
对于全部的数据,保证有n≤10000。
样例解析:三堆太少了,来一个六堆的解释一下
如6堆果子,每一堆的数量(排序后)分别是1 3 5 7 7 9
将1 3合并,消耗的力气是4。合并排序堆的情况是: 4 5 7 7 9
将 4 5合并,消耗的力气是9 。合并排序完的情况是:7 7 9 9
将 7 7合并,消耗的力气是14。合并排序完的情况是:9 9 14
将9 9合并,消耗的力气是18。合并排序完的情况是:14 18
最后将 14 和 18 合并,消耗的力气是32;
消耗的总力气是4+9+14+18+32=77.
其实本题思路很简单,把所有堆里面的,最小的两堆合并。合并完的那堆继续放到所有堆中排序,然后继续找出最小的两堆合并,直到最后只剩一堆,这样的力气最少。
然后发现了,优先队列这个好冻吸~
priority_queue<int, vector<int>, greater<int> >//小顶锥,即所有元素的都比锥顶大
priority_queue<int, vector<int>, less<int> > //大顶锥,即所有的元素都比锥顶小
基本操作:
q.push(num);//压入队列
q.pop();//将队列顶端出队
q.empty();//判空
q.size();//大小
num=q.top();//num是队列顶端元素
对优先数列有兴趣的小伙伴可以自己去了解,这里就不多探讨啦(其实是因为我自己只会基本操作,讲不清楚,废物)
他可以自动排序,而且当是小顶锥是q.top()输出的是队列的最小值,就很适合这个题。
在这一题当中,我们可以把优先队列前两个top取出,然后加起来,继续放进队列(他会自己排序)继续下一次取出前两个top,然后加起来,继续放进队列。。直到队列中只剩下一个元素。每一次取出两个top加起来的所发费的力气都加起来,就是最终的结果。
如果你已经有思路的话,可以动动手呀~
完整代码:
#include<bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int> >p;//优先队列,小顶锥
int main()
{
int n;
int a[10010];
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
p.push(a[i]);//将输入的堆的大小压入队列中,他会自动排好序
}
long long t=0;
int temp1,temp2;//每次从顶端取出两个最小的数
for(int i=1;i<=n-1;i++)
{
temp1=p.top(); p.pop();//取出顶端的数后,就把顶端的数出队
temp2=p.top(); p.pop();
p.push(temp1+temp2);//把两个加起来的和入队,继续进行下一次的排序
t=t+temp1+temp2;//每一次合并最小两端的所用的力气加起来就是最后的结果
}
printf("%d\n",t );
return 0;
}
评测平台:洛谷
这道题就是帮我们简单应用一下优先队列的~然后继续看看下面这题吧。
超市:
来源:https://www.acwing.com/problem/content/147/
描述:
超市里有N件商品,每件商品都有利润pi和过期时间di每天只能卖一件商品,过期商品不能再卖。
求合理安排每天卖的商品的情况下,可以得到的最大收益是多少。
输入格式
输入包含多组测试用例。
每组测试用例,以输入整数N开始,接下来输入N对pi和di,分别代表第i件商品的利润和过期时间。
在输入中,数据之间可以自由穿插任意个空格或空行,输入至文件结尾时终止输入,保证数据正确。
输出格式
对于每组产品,输出一个该组的最大收益值。
每个结果占一行。
数据范围
0≤N≤10000
1≤pi,di≤10000
最多有14组测试样例
输入样例:
4 50 2 10 1 20 2 30 1
7 20 1 2 1 10 3 100 2 8 2
5 20 50 10
输出样例:
80
185
思路:
1:利用优先队列q的大小即 p.size()表示经过了多少天
2:用一个结构体a表示pi(利润)和di(离过期所剩的天数)
3:先将结构体按di排序,di小的排在前面。这时di和p.size()有两种情况:
(1)当a[i].di>p.size()时,表示保质期还没到,这个东西还能卖,所以将a[i].pi(这个物品的价 值放进优先队列p)
(2)当a[i].di=p.size()时,表示该物品和前一个物品同一天过期,这时在已经入队的物品中找到价值最小的一个(p.pop( )),若p.pop()<a[i].pi,则把p.pop()出队,a[i].pi入队p.size()没变(用高利润的产品代替低利润的).a[i].pi入队,若x>a[i].pi,则继续比较下一个(嗯?嗯!它不配,利润没有前面的高,没必要花一天时间去卖掉它)
(3)想一想为什么没有a[i].di<p.size()的情况,提示一下,结构体di已经排序过了。
如果你现在已经有思路了,不妨动手过一下这题,说不定就AC了呢~
还是把完整代码奉上吧:
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
priority_queue<int,vector<int>,greater<int> >p;
int n;
struct qqueue{
int pi;
int di;
}a[N];
bool compare(qqueue a,qqueue b)
{
return a.di<b.di;
};
int main()
{
while(scanf("%d",&n)!=EOF)
{
if(n==0)
{
printf("0\n");
continue;
}
for(int i=0;i<n;i++)
{
scanf("%d%d",&a[i].pi,&a[i].di);//输入利润和过期时间
}
sort(a,a+n,compare);
for(int i=0;i<n;i++)
{
if(a[i].di>p.size())
p.push(a[i].pi);
else if(a[i].di==p.size()&&a[i].pi>p.top())
{
p.pop();
p.push(a[i].pi);
}
}
long long res=0;
while(!p.empty())
{
res+=p.top();
p.pop();
}
printf("%lld\n",res);
}
return 0;
}
评测平台:AcWing
看运行时间是不是觉得太慢了呢,那~期待下回分解。。。
欢迎大家友好评论,有啥问题可以dd我呀~