動態中位數 對頂堆

題目鏈接

https://www.acwing.com/problem/content/108/

依次讀入一個整數序列,每當已經讀入的整數個數爲奇數時,輸出已讀入的整數構成的序列的中位數。

輸入格式

第一行輸入一個整數P,代表後面數據集的個數,接下來若干行輸入各個數據集。

每個數據集的第一行首先輸入一個代表數據集的編號的整數。

然後輸入一個整數MM,代表數據集中包含數據的個數,MM一定爲奇數,數據之間用空格隔開。

數據集的剩餘行由數據集的數據構成,每行包含10個數據,最後一行數據量可能少於10個,數據之間用空格隔開。

輸出格式

對於每個數據集,第一行輸出兩個整數,分別代表數據集的編號以及輸出中位數的個數(應爲數據個數加一的二分之一),數據之間用空格隔開。

數據集的剩餘行由輸出的中位數構成,每行包含10個數據,最後一行數據量可能少於10個,數據之間用空格隔開。

輸出中不應該存在空行。

數據範圍

1≤P≤1000
1≤M≤9999

輸入樣例:

3 
1 9 
1 2 3 4 5 6 7 8 9 
2 9 
9 8 7 6 5 4 3 2 1 
3 23 
23 41 13 22 -3 24 -31 -11 -8 -7 
3 5 103 211 -311 -45 -67 -73 -81 -99 
-33 24 56

輸出樣例:

1 5
1 2 3 4 5
2 5
9 8 7 6 5
3 12
23 23 22 22 13 3 5 5 3 -3 
-7 -3

 題解

        爲了動態維護中位數,我們可以建立兩個二叉堆:一個小根堆,一個大根堆。再依次讀入這個整數序列的過程中,設當前序列長度爲M,我們始終保持:

1.序列從小到大排名爲1到M/2的整數保存在大根堆裏;

2.序列從小到大排名在M/2+1到M的整數保存在小根堆裏

   任何時候,如果某一個堆中元素過多,打破這個性質,就取出該堆的堆頂插入另一個堆。這樣一來,序列的中位數就是小根堆的堆頂。

   每次讀入一個數值X後,或X比中位數小,則插入到大根堆中,否則插入小根堆中,在插入之後檢查並維護上述性質即可。這就是“”對頂堆”算法。

代碼

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int main(){
   int t;
   cin>>t;
   while(t--){
    priority_queue<int> maxx;
    priority_queue<int,vector<int>,greater<int> > minn;
    int x,y;
    cin>>x>>y;
    cout<<x<<" "<<(y+1)/2<<endl;
    int cnt=0;
    for(int i=0;i<y;i++){
        int a;
        cin>>a;
        if(minn.empty()||minn.top()<a){
             minn.push(a);
             if(minn.size()>maxx.size()+1){
                int b=minn.top();
                minn.pop();
                maxx.push(b);
             }
        }else{
            maxx.push(a);
            if(maxx.size()>minn.size()){
                int b=maxx.top();
                maxx.pop();
                minn.push(b);
            }
        }
        if(i%2==0){
            cout<<minn.top()<<" ";
            cnt++;
        }
        if(cnt==10){
            cnt=0;
            cout<<endl;
        }
    }
    if(cnt!=0) cout<<endl;
   }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章