洛谷1168:中位数(对顶堆)
题目描述:
- 给定一个长度为(N)的非负整数序列(A_i),对于所有((1leq kleqfrac{N+1}{2})),输出(A_1,A_3,...,A_{2k-1})的中位数。即前(1,3,4,...)个数的中位数。
输入格式:
- 第一行为一个整数(N),表示序列长度
- 第二行输入(N)个非负整数(A_i)((A_ileq10^9))。
输出格式:
- 共(frac{N+2}{2})行,其中第(i)行为(A_1,A_2,A_3,...,A_{2k-1})的中位数。
思路:
-
对顶堆:处理动态中位数等问题,灵活地运用了堆的性质,本质上是维护两个堆。
- 大根堆(Q_1):维护集合中较小值的部分。
- 小根堆(Q_2):维护集合中较大值的部分。
- 可以知道其中大根堆的所有元素都小于小根堆。
-
插入操作:
- 当需要插入的数字(x)大于大根堆堆顶时,插入小根堆。
- 否则插入大根堆。
-
维护操作:
- 当两个堆的大小之差的绝对值大于(1)时。
- 如果大根堆的(size)大于小根堆的(size),则将大根堆的堆顶插入小根堆并让大根堆执行(pop)操作。
- 否则小根堆的堆顶插入大根堆并让小根堆执行(pop)操作。
-
输出结果:
- 当我遍历到的数字是奇数时,输出(size)较大的堆的堆顶。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int x, n;
priority_queue<int> q1; //大根堆
priority_queue<int, vector<int>, greater<int> > q2; //小根堆
int main()
{
scanf("%d%d", &n, &x);
q1.push(x); //初始值插入大根堆中
printf("%d
", x);
for(int i = 2; i <= n; i++)
{
scanf("%d", &x);
//插入操作
if(x > q1.top()) q2.push(x);
else q1.push(x);
//维护操作
while(abs((int)q1.size() - (int)q2.size()) > 1)
{
if(q1.size() > q2.size())
{
q2.push(q1.top());
q1.pop();
}
else
{
q1.push(q2.top());
q2.pop();
}
}
//输出结果
if(i&1) printf("%d
", q1.size() > q2.size() ? q1.top() : q2.top());
}
return 0;
}