C - Running Median (HDU - 3282)
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.
Input
The first line of input contains a single integer P, (1 ≤ P ≤ 1000), which is the number of data sets that follow. The first line of each data set contains the data set number, followed by a space, followed by an odd decimal integer M, (1 ≤ M ≤ 9999), giving the total number of signed integers to be processed.
The remaining line(s) in the dataset consists of the values, 10 per line, separated by a single space.
The last line in the dataset may contain less than 10 values.
Output
For each data set the first line of output contains the data set number, a single space and the number of medians output (which should be one-half the number of input values plus one). The output medians will be on the following lines, 10 per line separated by a single space. The last line may have less than 10 elements, but at least 1 element. There should be no blank lines in the output.
Sample Input
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
Sample Output
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
题目大意
多组输入,每当输入的个数为奇数时输出此时排序好的中位数。
题目分析
使用优先队列的方法建立对顶堆,保持两个二叉堆的数据个数相差不大于 1。当输入个数为奇数时,输出数据个数较大的那一个的 top 元素。
两个二叉堆的堆顶其中之一必为中位数,因为小根堆小到大,大根堆
从大到小,将堆顶放在一起相当于是将一个有序序列从中间断开,断开位置即为中位数。
一般对顶堆是大根堆在上,小根堆在下,而我相反(哎呀无所谓了,做出题就行),这个玩意是可以动态维护第k大的值(比如中位数)
每次操作logn,注意小根堆里面最小的还要比大根堆里面最大的要大。
首先把第一个数放入小根堆里面,第二个数进行判断,如果小于小根堆,就放入大根堆里面,否则还放入小根堆里面。注意,每放入一次,都要判断,如果小根堆元素个数比大根堆多二,就把小根堆top放入大根堆里面,然后删掉小根堆这个top,相当于把这个元素下移了一位。
每次i为奇数时,输出小根堆里面的top,这个top就是中位数。
错误代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
int n,cnt,len,d,x,jishu;
priority_queue<int> max_heap; //大根堆建立
priority_queue<int,vector<int>,greater<int>> min_heap; //小根堆建立
int main()
{
cin>>n;
while(n--)
{
jishu=0;
cin>>cnt>>len;
cout<<cnt<<" "<<(len+1)/2<<endl;
for(int i=1;i<=len;i++)
{
cin>>x;
if(i==1) min_heap.push(x); //第一个放入小根堆
else
{
if(x<min_heap.top()) max_heap.push(x);
else min_heap.push(x);
if(max_heap.size()>min_heap.size())
{
min_heap.push(max_heap.top());
max_heap.pop();
}
if(min_heap.size()-max_heap.size()>2)
{
max_heap.push(min_heap.top());
min_heap.pop();
}
}
if(i%2==1)
{
jishu++;
if(jishu%10!=1) cout<<" ";
cout<<min_heap.top();
if(jishu%10==0) cout<<'\n';
}
}
while(!max_heap.empty()) max_heap.pop(); //清空堆
while(!min_heap.empty()) min_heap.pop();
if(jishu%10!=0) cout<<endl;
}
return 0;
}
实际上,我的代码中间对空队列用了size是错误的,空队列不能用size,top,可以在自己编译环境下试一试,是行不通的,但是好像也能过,嘿嘿。
一个队列首先要对其进行是否为empty的判断,再进行top,size的使用。
修改后代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
int n,cnt,len,d,x,jishu;
priority_queue<int> max_heap;
priority_queue<int,vector<int>,greater<int>> min_heap;
int main()
{
cin>>n;
while(n--)
{
jishu=0;
cin>>cnt>>len;
cout<<cnt<<" "<<(len+1)/2<<endl;
for(int i=1;i<=len;i++)
{
cin>>x;
//修改处开始
if(i%2==1)
{
if(max_heap.empty()) min_heap.push(x);
else
{
if(x>max_heap.top()) min_heap.push(x);
else min_heap.push(max_heap.top()),max_heap.pop(),max_heap.push(x);
}
}
else
{
if(x<min_heap.top()) max_heap.push(x);
else max_heap.push(min_heap.top()),min_heap.pop(),min_heap.push(x);
}
//修改处截至
if(i%2==1)
{
jishu++;
if(jishu%10!=1) cout<<" ";
cout<<min_heap.top();
if(jishu%10==0) cout<<'\n';
}
}
while(!max_heap.empty()) max_heap.pop();
while(!min_heap.empty()) min_heap.pop();
if(jishu%10!=0) cout<<endl;
}
return 0;
}