关于priority_queue的运用(两道例题:序列合并 + 最小函数值)

首先列举一下基础的queue的用法


queue入队,如例:q.push(x); 将x 接到队列的末端。

queue出队,如例:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。

访问queue队首元素,如例:q.front(),即最早被压入队列的元素。

访问queue队尾元素,如例:q.back(),即最后被压入队列的元素。

判断queue队列空,如例:q.empty(),  队列空时,返回true。

访问队列中的元素个数,如例:q.size()


然后则是priority_queue的特别的地方:

      这个东西就是堆,如果你要申请一个堆的话最基本的语句如下:

     priority_queue<int> q;  (这个int就是你要申请的类型, 此时默认的为大根堆,即依次弹出的数的顺序为从大到小)

     如果要从小到大,则是这样:

     priority_queue<int, vector<int>, greater<int> > q;

    所以结构体就很容易想到了啊:

          priority_queue<lpl>  (lpl 为结构体的类型)

但是由于是结构体,那么它的比较大小的方式则也需要你来定义,所以你需要重载运算符(具体的方法看接下来的例题代码)


T1:

           

题目描述

有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到N^2个和,求这N^2个和中最小的N个。

输入输出格式

输入格式:

第一行一个正整数N;

第二行N个整数Ai,满足Ai<=Ai+1且Ai<=10^9;

第三行N个整数Bi, 满足Bi<=Bi+1且Bi<=10^9.

【数据规模】

对于50%的数据中,满足1<=N<=1000;

对于100%的数据中,满足1<=N<=100000。

输出格式:

输出仅一行,包含N个整数,从小到大输出这N个最小的和,相邻数字之间用空格隔开。

输入输出样例

3
2 6 6
1 4 8
3 6 7











这道题是一道很经典的问题,在刘汝佳老师的蓝书上的优先队列中可以找到,可以转化为多路归并问题。

这道题的思路如下:

先将a1与b中的每一个元素的和放入堆中;

然后进行n次循环

{

弹出堆顶的元素(因为是小根堆,所以为最小值)

输出这个元素

假设这个元素是a[n] + b[n]

将a[n + 1] 和 b[n] 的和放在堆中

}

以上是整个程序的模拟过程,更加细节的原理大家可以自行脑补一下2333(当然看一下书就更好了)

(同时,这个问题的升级版在书上也有详细提及哦~)


代码奉上:


#include<cstdio>
#include<queue>

using namespace std;
const int maxn = 1e5 + 5;
struct lpl
{
	int a_i;
	int b_i;
	int sum;
}lin;

bool operator < (lpl a, lpl b)
{
	if(a.sum > b.sum)	return true;
	return false;
}

priority_queue<lpl> q;

int n;
int a[maxn], b[maxn];

inline void putit()
{
	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)
		lin.a_i = 1, lin.b_i = i, lin.sum = a[1] + b[i], q.push(lin);
}

inline void workk()
{
	for(int i = 1; i <= n; ++i)
	{
		lpl now = q.top();
		q.pop();
		printf("%d ", now.sum);
		now.sum = a[now.a_i + 1] + b[now.b_i];
		now.a_i++;
		q.push(now);
	}
}

int main()
{
	putit();
	workk();
}



T2:



题目描述

有n个函数,分别为F1,F2,...,Fn。定义Fi(x)=Ai*x^2+Bi*x+Ci (x∈N*)。给定这些Ai、Bi和Ci,请求出所有函数的所有函数值中最小的m个(如有重复的要输出多个)。

输入输出格式

输入格式:

输入数据:第一行输入两个正整数n和m。以下n行每行三个正整数,其中第i行的三个数分别位Ai、Bi和Ci。Ai<=10,Bi<=100,Ci<=10 000。

输出格式:

输出数据:输出将这n个函数所有可以生成的函数值排序后的前m个元素。这m个数应该输出到一行,用空格隔开。

说明

数据规模:n,m<=10000

输入输出样例

输入样例
3 10
4 5 3
3 4 5
1 7 1
9 12 12 19 25 29 31 44 45 54















这道题只要提前分析一点东西就好了:

首先,这是个二次函数,对吧?要求最值,对吧?所以要求对称轴,对吧?

好,因为所有数都是正整数的,所以 x = -b/2a 一定小于0

所以这些所有函数在0到正无穷范围内是单调递增的


提示给到这里,再加上我们要讲的东西以及上一道例题,聪明的你一定想出来了吧:


再次奉上代码:


#include<cstdio>
#include<queue>

using namespace std;

struct lpl
{
	int num;
	int sum;
	int rank;
}lin;

bool operator < (lpl a, lpl b)
{
	if(a.sum > b.sum)	return true;
	return false;
}

const int maxn = 1e4 + 5;
int n, m;
int a, b, c;
int aa[maxn], bb[maxn], cc[maxn];
priority_queue<lpl> q;

inline int f(int a, int b, int c, int x)
{
	return a * x * x + b * x + c;
}

inline void workk()
{
	for(int i = 1; i <= n; ++i)
	{
		lin.sum = f(aa[i], bb[i], cc[i], 1);
		lin.num = i;
		lin.rank = 1;
		q.push(lin);
	}
	for(int i = 1; i <= m; ++i)
	{
		lin = q.top();
		q.pop();
		printf("%d ", lin.sum);
		lin.sum = f(aa[lin.num], bb[lin.num], cc[lin.num], lin.rank + 1);
		lin.rank++;
		q.push(lin);
	}
}

inline void putit()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i)
	{
		scanf("%d%d%d", &aa[i], &bb[i], &cc[i]);
	}
}

int main()
{
	putit();
	workk();
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值