优先队列priority_queue

近期在学习一些容器和其应用算法 稍微做了一些总结

优先队列又被叫做二叉堆或者堆因为其底层实现是靠排序二叉树来实现的是一个很实用的容器 一般的优先队列的题目都是一些思维题或者和贪心结合的题目 优先队列一般分为大根堆和小根堆意思就是从大到小排序和从小到大排序

priority_queue<int,vector<int>,less<int> >//大根堆
priority_queue<int,vector<int>,greater<int> >//小根堆

若要对结构体放入优先队列需要对<进行重载

下面是一些常见题目

1.合并果子

题目描述

    在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

    每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

    因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。

输入描述:

输入包括两行,第一行是一个整数n(1<=n<=10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。 

输出描述:

输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于231。

示例1

输入

复制3 1 2 9

3
1 2 9

输出

复制15

15

备注:

对于30%的数据,保证有n<=1000:
对于50%的数据,保证有n<=5000;
对于全部的数据,保证有n<=10000。

这就是一个很简单优先队列问题按照ai放入优先队列这里用小根堆(越小体力越小)每次取出两个加和之后再放入

#include<bits/stdc++.h>
using namespace std;
int a[10010],n;
int main(){
    queue<long long>q1,q2;
    long long x[3],ans=0;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++)q1.push(a[i]);
    for(int i=1;i<n;i++){
        for(int j=1;j<=2;j++){
            if(q2.empty()||(!q1.empty()&&q2.front()>q1.front())){
                x[j]=q1.front();
                q1.pop();
            }
            else{
                x[j]=q2.front();
                q2.pop();
            }
        }
        q2.push(x[1]+x[2]);
        ans+=x[1]+x[2]; 
    }
    cout<<ans<<endl;
} 

2.第k小

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述 
有一个长度为n的数组,值为 a[i], 牛牛想找到数组中第 k 小的数。比如 1 2 2 3 4  6 中,第 3 小的数就是2.
牛牛觉得这个游戏太简单了,想加一点难度,现在牛牛有 m 个操作,每个操作有两种类型。
1 x  1 代表操作一,给数组中加一个元素 x 。(0 ≤ x ≤ 1e9)
2     2 代表操作二,查询第 k 小的数。如果没有 k 个数就输出−1
输入描述:
第一行有三个整数,n m k,(1≤n,m,k≤2e5)
第二行包含 n 个整数 a[i] ( 0 ≤ a[i] ≤ 1e9)
接下来m行,每行代表一个操作。具体见题目描述
输出描述:
每次查询输出一个第  k  小的数。


示例1
输入
复制
5 4 3
1 2 3 4 5
2
1 1
1 3
2
输出
复制
3
2

思路:没什么很难的 思路来自于快速排序 我们既然要求第k大那么创建一个优先队列(大顶堆)只放前k小的元素 之后每次插入操作 先将元素插入队列 再删除队头
即可

#include<bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,less<int> >q;
int main(){
	int n,M,k,x,ans;
	scanf("%d%d%d",&n,&M,&k);
	for(int i=1;i<=n;i++){
		scanf("%d",&x);
		if(q.size()<k)q.push(x);
		else{
			q.push(x);
			q.pop();
		}
	}
	int order;
	while(M--){
		cin>>order;
		if(order==2){
			if(q.size()==k)printf("%d\n",q.top());
			else printf("-1\n");
		}
		else{
			scanf("%d",&x);
			if(q.size()<k)q.push(x);
			else{
				q.push(x);
				q.pop();
			}
		}
	}
}

3.tokitsukaze and Soldier (贪心)

在一个游戏中,tokitsukaze需要在n个士兵中选出一些士兵组成一个团去打副本。
第i个士兵的战力为v[i],团的战力是团内所有士兵的战力之和。
但是这些士兵有特殊的要求:如果选了第i个士兵,这个士兵希望团的人数不超过s[i]。(如果不选第i个士兵,就没有这个限制。)
tokitsukaze想知道,团的战力最大为多少。

思路:像这种题目我们一般用到的想法都是定一个变量然后去判断另一个变量 这里我们选择定限制人数 我们可以发现 一个队里的限制人数就是最小的限制人数 所以
我们从大到小枚举(因为如果枚举小的限制人数的那么前面的人数都可以加)我们先对士兵的限制人数从大到小排序 之后我们便开始枚举 如果 q.size()<限制人数那么
我们直接放入对应战力即可(这里用到小顶队)否则我们将当前的士兵放入然后再删除队首(最小的战力)即可

#include<bits/stdc++.h>
using namespace std;
priority_queue<long long,vector<long long>,greater<long long> >q;
struct node{
	long long s,v;
}a[1000010];
bool cmp(node a,node b){
	return a.s>b.s;
}
int main(){
	long long n,ans=0,sum=0;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i].v>>a[i].s;
	sort(a+1,a+n+1,cmp);
	long long i=1,now;
	while(i<=n){
		now=a[i].s;
		while(a[i].s==now){
			q.push(a[i].v);
			sum+=a[i].v;
			while(q.size()>now){
				sum-=q.top();
				q.pop();
			}
			i++;
		}
		ans=max(ans,sum);
	}
	cout<<ans<<endl;
} 

4.小A和背包(贪心+二分)

小A手头有 n 份任务,他可以以任意顺序完成这些任务,只有完成当前的任务后,他才能做下一个任务
第 i 个任务需要花费  x_i的时间,同时完成第 i 个任务的时间不能晚于 y_i,时间掌控者向小A提出了一个条件:如果完成第 i 个任务的时间本应是 t ,
但小A支付 m 个金币的话,他可以帮助小A在 t-m\times z_it−m×z i时刻完成第 i 个任务, z_i是时间参数,会在输入中给出小A想按时完成所有任务,
请你帮他制定一个花费金币最少的方案 注意:不能使得某个任务的花费时间小于 0 ,花费的金币可以不是整数

思路:
首先我们按照截止时间排序因为截止时间短的一定优先做 然后优先队列里存的是按照z参数排序的大根堆(参数大花费少)然后循环每一个任务首先让积累时间+当前
任务需要完成的时间然后如果这个时间小于截止时间说明可以直接完成否则我们需要去花钱 那么就从队列的队头取出然后比较队头的完成需要的时间和当前
任务截止时间减去积累的时间的绝对值 如果前者小说明这个任务队头就算不花时间还是不能完成这个任务 那么再从队里取出队头 周而复试即可 如果后者小 那么只要
话后者的钱即可

#include<bits/stdc++.h>
using namespace std;
struct node{
	int x,y,z;//x代表任务时间 y代表截止时间 z代表花费参数 
	bool operator < (const node&n)const{
		return z<n.z;
	} 
}a[200010];
bool cmp(node n1,node n2){
	return n1.y<n2.y;
}
priority_queue<node>q;
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i].z>>a[i].x>>a[i].y;
	sort(a+1,a+1+n,cmp);
	int time=0;
	double ans=0;
	for(int i=1;i<=n;i++){
		q.push(a[i]);
		time+=a[i].x;
		while(a[i].y<time){
			node p=q.top();
			q.pop();
			int temp=min(time-a[i].y,p.x);
			time-=temp;
			ans+=temp*1.0/p.z;
			p.x-=temp;
			if(p.x)q.push(p);
		}
	}
	printf("%.1lf\n",ans);
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值