luogu_1168: 中位数

洛谷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;
}

原文地址:https://www.cnblogs.com/zxytxdy/p/11633248.html