首先列举一下基础的queue的用法:
queue入队,如例:q.push(x); 将x 接到队列的末端。
queue出队,如例:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。
访问queue队首元素,如例:q.front(),即最早被压入队列的元素。
访问queue队尾元素,如例:q.back(),即最后被压入队列的元素。
判断queue队列空,如例:q.empty(), 队列空时,返回true。
访问队列中的元素个数,如例:q.size()
这个东西就是堆,如果你要申请一个堆的话最基本的语句如下:
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();
}
题目描述
有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();
}