题目链接:https://ac.nowcoder.com/acm/problem/50940
题意:有n个数,每次给一个,让你输出目前已经给你的数的中位数,只在拥有奇数个数字时输出。
思路:分析题意知道,每当我们拥有的数量为奇数时,就要输出中位数,暴力一点就是,每当我们有奇数个数字的时候就排一次序,然后输出中间的那个数。这个太暴力。仔细想想会知道,其实没必要排序,我只要知道中间那个数就好,至于比这个数小的数是按照一个什么顺序排列,与本题无关。
那么就能联想到之前学到的堆这种数据结构。用一个大根堆来保存小的那些,用小根堆来存大的那部分,因为我们每次要输出的中位数,我们只要确定它的位置就好了,即它是比当前中位数大还是小。当大小根堆中元素数量差距比一大时,就要从元素多的那边移动一个数到另外一个根堆。每次输出的时候把根堆中元素数量为奇数的那个的堆顶输出就好。至此代码也就写出来了。要注意代码里面的n和m的意义和上面写的思路里面的意义不一样。m才是总个数。
//author CN.TTDragon #include<bits/stdc++.h> typedef long long ll; const ll mod=1e9+7; const ll maxn=1e5+7; const double pi=acos(-1); using namespace std; priority_queue<int,vector<int>, greater<int> > x; //小根堆 ,存较大的一半的数 priority_queue<int> d; //大根堆 ,存较小的一半的数 int main() { int t; scanf("%d",&t); while(t--) { while(!x.empty()) { x.pop(); } while(!d.empty()) { d.pop(); } int n,m; scanf("%d %d",&n,&m); //n是编号 m是个数 printf("%d %d\n",n,(m+1)/2); int q; scanf("%d",&q); printf("%d ",q); d.push(q); int cnt=1; for(int i=0;i<m-1;i++) { int a; scanf("%d",&a); //大根堆,堆顶元素是最大的 //如果比这个数小,放进大根堆 //反之放进小根堆 if(a<=d.top()) { d.push(a); } else { x.push(a); } int numx=x.size(); int numd=d.size(); //小根堆的元素多了 if(numx-numd>1) { d.push(x.top()); x.pop(); numx--; numd++; } if(numd-numx>1) { x.push(d.top()); d.pop(); numd--; numx++; } if(i%2==1) { //中位数在大根堆 if(numd>numx) { printf("%d ",d.top()); } else { printf("%d ",x.top()); } cnt++; //每十个数换一次行,当为最后一个数的时候不需要换行 if(cnt%10==0&&i!=m-2) { cnt=0; printf("\n"); } } } printf("\n"); } return 0; }
写在最后:
1,我也不知道为啥同样的代码交上去,一个MLE,一个AC了,挺离谱的。
2,第一次体会到scanf的速度大于cin。
3,昨天写的3.25,我还以为堆这种结构会把最小的数排在顶上,结果今天一看大佬们写的才明白大小根堆的区别就是顶部元素的不同。赶紧改正,差点闹了大笑话。太菜了~