POj-3784 Running Median-對頂堆
阿新 • • 發佈:2018-11-26
問題描述:
For this problem, you will write a program that reads in a sequence of 32-bit signed integers. After each odd-indexed value is read, output the median (middle value) of the elements received so far.
題目大意:
給出一個序列,輸出其子序列
AC程式碼:
int main()
{
int t;
cin >> t;
while(t--)
{
priority_queue<int,vector<int>,less<int> > pq1;//大頂堆
priority_queue<int,vector<int>,greater<int> > pq2;//小頂堆
int id,n;
cin >> id >> n;
printf("%d %d\n",id,n / 2 + 1);//中位數的總數
int cnt = 0;//每輸出10個數換行一次
for(int i = 1; i <= n; ++i)
{
int t;
cin >> t;
if(i % 2 == 1)//如果是第奇數個數
{
pq2.push(t);//加入小頂堆
while(!pq1.empty() && pq1.top() > pq2.top())//維護對頂堆的性質,注意第一次大頂堆為空
{
//交換堆頂元素
int t1 = pq1.top();
pq1.pop();
int t2 = pq2.top();
pq2.pop();
pq1.push(t2);
pq2.push(t1);
}
printf("%d ",pq2.top());//輸出小頂堆對頂
cnt++;
if(cnt % 10 == 0)
cout << endl;
}
else//第偶數個數放入大頂度
pq1.push(t);
}
cout << endl;
}
return 0;
}
解決方法:
對於一個序列中的中位數,它左邊的數都比它小,右邊的數都比它大。比較暴力的想法是對每一個要求的序列都進行一次快排,輸出中間的數。這樣非常地慢,而且資訊過多(升序)是一種浪費,對於左邊的數,只要比中位數小就可以了,沒必要有序。
根據數學中的如果一個集合A中最小的數比另一個集合B中最大的數大,則A中的所有數大於B中的所有數。
集合中的最大最小數容易讓我們聯想到堆這種資料結構
最小堆的堆頂要大於最大堆的堆頂,而如果最小堆的大小比最大堆的大小多1的話,最小堆的堆頂就是中位數!
最小堆與最大堆之間的關係在中位數的輸出之前維護即可