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号
浙公网安备 33010602011771号