STL 之 优先队列、Vector、Set及其重要经典例题2——优先队列 priority_queue(详细分析)

可以观看此ppt,里面的题解析详细。
点这里



接着 上一篇 说…

二、优先队列

基础内容

概念

priority_queue翻译为优先队列,一般用来解决一些贪心问题,其底层是用堆来实现的。在优先队列中,任何时刻,队首元素一定是当前队列中优先级最高的那一个。使用优先队列,也必须加#include < queue>及using namespace std;。

注意:和queue不一样的是,priority_queue没有front()和back(),而只能通过top()或pop()访问队首元素(也成为堆顶元素),也就是优先级最高的元素。

优先队列优先级如何设置:

大根堆优先队列的定义:
priorty_queue q; //默认为大顶堆优先队列
priorty_queue q;

小根堆优先队列的定义:
priorty_queue q;

。。。其他基础内容请观看ppt
点这里


例题


①有序表的最小和

题目描述

给出两个长度为 n 的有序表 A 和 B,在 A 和 B 中各任取一个元素,可以得到 n*n 个和,求这些和中最小的 n 个。

输入格式
第 1 行包含 1 个整数正 n(n≤400000)。 第 2 行与第 3 行分别有 n 个整数,各代表有序表 A 和 B。一行中的每两个整数之间用一个空格隔开,大小在长整型范围内,数据保证有序表单调递增。

输出格式
输出共 n 行,每行一个整数,第 i 行为第 i 小的和。 数据保证在 long long 范围内。

样例

【输入样例】
3
1 2 5
2 4 7
【输出样例】
3
4
5

算法分析

如果用枚举出所有和,把它们依次压入小根堆优先队列,取出n个堆顶元素即可,但n的范围是400000,n方的算法想都不用去想了,由于两个序列是从小到大排序了的,我们来分析一下下表的数据:

 第一行 A[1]+B[1] ≤ A[1]+B[2] ≤ A[1]+B[3] ≤ ······
 第二行 A[2]+B[1] ≤ A[2]+B[2] ≤ A[2]+B[3] ≤ ······
 ······
 第n行 A[n]+B[1] ≤ A[n]+B[2] ≤ A[n]+B[3] ≤ ······
显然,每一行的第一项和都是该行最小的和,我们不妨把每一行的第一项和(共n项)先压入优先队列,取出堆顶元素(最小值)并输出,如果取的是第i行的元素,就把第i行的下一个元素压入,让堆中始终保持n个元素和。
时间复杂度分析:一重循环压入每行第一个元素O(n),一重循环循环n次出队以及压入该行下一个元素O(nlog2n),总时间复杂度:O(n+nlog2n)≈ O(n*log2n)

代码

#include
#include
#include
#include
using namespace std;
const int M=400005;
int a[M],b[M],p[M];
struct node{
	int f,val;
	bool operator<(const node x) const{
		return val>x.val;
	}//运算符重置。不用慌,只是一个格式。
};
priority_queue<node> q;
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&b[i]);
	}
	for(int i=1;i<=n;i++){
		p[i]=2;
		node x;
		x.val=a[i]+b[1],x.f=i,q.push(x);
	}
	for(int i=1;i<=n;i++){
		node x=q.top();
		q.pop();
		printf("%d\n",x.val);
		x.val=a[x.f]+b[p[x.f]++];
		q.push(x);
	}
	return 0;
}

②桐桐的新闻系统

题目描述

桐桐为期末的计算机作业设计了一套新闻系统,他把这套系统称为Argus。
使用这套系统的用户可以向这套系统注册,然后这套系统就会以用户要求发送新闻的时间间隔向用户发送一次新闻。
向Arugs注册的指令具有以下格式:Register Q_num Period
Q_num(0Q_num 3000)是用户的ID,Period()是间隔。注册后Period秒,结果会第一次到达。
所有的用户都有不同的Q_num。桐桐测试了一段时间后,想知道系统前K次给谁发送新闻了。如果同一时间发送多个新闻,以Q_num的升序排列。

输入格式
第一部分是注册指令,每条一行。指令数不超过10000,所有指令同时执行完。此部分以“#”结束。
第二部分仅一行一个整数。

输出格式
输出前K个新闻发送到的用户的Q_num,每行一个。

样例

####输入样例
Register 2004 200
Register 2005 300
#
5
####输出样例
2004
2005
2004
2004
2005

算法分析

我们用一个优先队列来存储用户和间隔时间,即:

struct node{
	int id;
	int k,tot;
	bool operator<(const node x) const{
		if(tot!=x.tot){
			return tot>x.tot;
		}
		return x.id<id;
	}
};
priority_queue<node> q;

为何用优先队列?

anser: 因为优先队列可以一直排序。

代码

#include
#include
#include
#include
#include
using namespace std;
struct node{
	int id;
	int k,tot;
	bool operator<(const node x) const{
		if(tot!=x.tot){
			return tot>x.tot;
		}
		return x.id<id;
	}
};
priority_queue<node> q;
int main(){
	char s[15];
	int n=0;
	while(scanf("%s",s)!=EOF&&s[0]!='#'){
		node x;
		cin>>x.id;
		cin>>x.k;
		x.tot=x.k;
		q.push(x);
	}
	int m;
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		node y;
		y.id=q.top().id;
		y.tot=q.top().k + q.top().tot;
		y.k=q.top().k;
		cout<<y.id<<endl;
		q.pop();
		q.push(y);
	}	
}

你可能感兴趣的:(STL)