P1168 中位数
P1168 中位数
题目简述
给出一个长度为\(N\)的非负整数序列\(A_i\),对于所有\(1 ≤ k ≤ (N + 1) / 2\),输出\(A_1, A_1 \sim A_3, …,A_1 \sim A_{2k - 1}\)的中位数。即前\(1,3,5,…\)个数的中位数。
思路
很不错的一道题目,有多种解法与思考
方法一:
对于数列前半段用大根堆维护,后半段用小根堆维护,每次只需要和堆顶比较即可
方法二:
利用 stl 中的 vector 可以快速实现插入操作,借助这题也可以熟悉 vector 和 upper_bounder 命令
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。
通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。
通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
方法三:
树状数组,先离散化处理,每次查询过程中,对于任意一个数 x,可以用倍增思路求出比x小的个数有多少,如果恰好是 \((i+1)/2\) 那么就是我们要找的数
代码
方法一
#include<bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int> >q1;//大根堆
priority_queue<int,vector<int>,greater<int> >q2;//小根堆
int main(){
int n;
cin>>n;
int x;
cin>>x;
q1.push(x);
cout<<x<<endl;
for(int i=2;i<=n;i++){
cin>>x;
if(q1.top()>x)q1.push(x);
else q2.push(x);
while(q1.size()+1<q2.size()){
q1.push(q2.top());
q2.pop();
}
while(q1.size()+1>q2.size()){
q2.push(q1.top());
q1.pop();
}
if(i%2)cout<<q2.top()<<endl;
}
return 0;
}
方法二
#include<bits/stdc++.h>
using namespace std;
vector<int> q;
int main(){
freopen("1168.in","r",stdin);
freopen("1168.out","w",stdout);
int n;
cin>>n;
for(int i=1;i<=n;i++){
int x;
cin>>x;
q.insert(upper_bound(q.begin(),q.end(),x),x);
if(i%2)cout<<q[(i-1)/2]<<endl;
}
return 0;
}
方法三(PS: 方法三本人未写,直接从题解搬来一篇了)
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,tot;
const int maxn=1e5+10;
int bit[maxn];
int a[maxn],b[maxn];
inline int lowbit(int x)
{
return x&-x;
}
inline void add(int pos,int x)
{
for(int i=pos;i<=tot;i+=lowbit(i))bit[i]+=x;
}
inline int find_kth(int k)
{
int ans=0,now=0; //这里主要解释一下这个的原理 ans就是答案,now是比当前找到的数的小的数字的个数。
for(int i=20;i>=0;i--) //2^20可以说很大了,满足我们的需求了,我们按照20倍增就可以
{
ans+=(1<<i); //先让答案加上去,试试
if(ans>tot||now+bit[ans]>=k)ans-=(1<<i);//如果超了总体的最大值(防止数组越界),或者是 超过了k个,就退回去,这里注意是大于等于,因为要考虑有重复元素,所以我们找的其实是一个满足小于他的个数小于k的最大数。。(可能不好理解,跑两遍样例就行了)
else now+=bit[ans];//能加就加上,这里不用怕加到了原来的数,因为树状数组的结构使这个新倍增出来的数就是多出来的那一条枝
}
return ans+1;//然后加上1就是答案啦
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[++tot]); //读个入
b[tot]=a[tot];
}
sort(a+1,a+1+n); //排个序
tot=unique(a+1,a+1+tot)-a-1; //去个重
for(int i=1;i<=n;i++)b[i]=lower_bound(a+1,a+1+tot,b[i])-a;//离散化一下
for(int i=1;i<=n;i++)
{
add(b[i],1); //动态加点
if(i&1)printf("%d\n",a[find_kth((i+1)>>1)]);//查kth
}
return 0;
}

浙公网安备 33010602011771号