优先队列入门题

优先队列入门题

合并果子

来源: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我呀~

猜你喜欢

转载自blog.csdn.net/m0_46288176/article/details/109268186