洛谷P3781 [TJOI2010] Middle 中位数 详细题解

查看原题请进入传送门:https://www.luogu.org/problem/show?pid=3871

先说说这道题的做法:

需要用两个堆来维护中位数左右的数!

一个大根堆bigheap( bhp )维护中位数mid左( 比mid小 )的数,队首( bhp[0] )表示离当前mid最接近但小于mid的数。
一个小根堆smallheap( shp )维护中位数mid右( 大于等于mid )的数,队首( shp[0] )表示离当前mid最接近但大于等于mid的数。
这样当前中位数便有两种情况:
1.数字总数为奇数:  mid=shp[0];
2.数字总数为偶数:  mid=min( bhp[0]+shp[0] )

操作:

1.先将初始数据二分建堆,我这里用了个笨方法用数组把初始数据存了排序后建堆( 因为要保证单调递增 )...
2.每当插入一个数据,对比当前中位数,如果比当前mid小就拟加入大根堆,否则拟加入小根堆...
3.那么问题来了:将新的点插入大/小根堆后,如果大根堆数据数比小根堆大1,或者小根堆数据数比大根堆大2( 因为总要保证小根堆数据数>=大根堆数据数 ),这时便要将新数加入建堆后,将被加入的堆的顶点移动到另一个堆中(作为另一个堆的新的顶点),以保证两个堆数据相对平衡。

那么问题来了:你们会手写堆吗?

如果会写,你们能写多少行呢?所以洗洗睡吧(误)
------于是就想到用STL的堆:
调用<algorithm>和<vector>库
vector <long long> a;
a.push_back();将数据加入vector
make_heap(a.begin(),a.end(),cmp);//建堆,这里的cmp判断为确定堆的种类( 需要手写 )
push_heap(a.begin(),a.end(),cmp);//维护这个堆
pop_heap(a.begin(),a.end(),cmp);//将顶点移动至队列最尾,并维护堆,但某数依然在队列最尾=_=
a.pop_back();//与上面的构成组合技,删除末尾的点
利用上述函数就可以模拟堆了

贴上代码:

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
vector <long long> bhp,shp;
long long n,bcnt,scnt,a[100010];//bcnt,scnt记录当前两个堆里数据数量 
bool bcmp(long long x,long long y)//大根堆的判断要写小于号! 
{
    return x<y;
}
bool scmp(long long x,long long y)//小根堆的判断要写大于号!
{
    return x>y;
}
int main()
{
    long long num; //下面的一小段搞定初始数据 
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
    {
        if(i<=n/2)
        bhp.push_back(a[i]);
        else
        shp.push_back(a[i]);
    }
    bcnt=n/2;scnt=n-n/2;
    make_heap(bhp.begin(),bhp.end(),bcmp);
    make_heap(shp.begin(),shp.end(),scmp);
    
    long long q,t;//t只是一个记录数据的rbq(误)变量 
    string ins;
    scanf("%lld",&q);
    for(int i=1;i<=q;i++)
    {
        cin>>ins;
        if(ins[0]=='a')
        {
            if(n%2==0)
                t=min(shp[0],bhp[0]);
            else 
                t=shp[0];//上面这一小段取出当前mid 
                
            n++;
            scanf("%lld",&num);
            //cout<<"num="<<num<<endl; 
            if(t-num>0) //拟加入大根堆
            {
                bcnt++;
                bhp.push_back(num);
                push_heap(bhp.begin(),bhp.end(),bcmp);
                if(bcnt-scnt>=1)//如果大根堆数据数过多 
                {
                    t=bhp[0];
                    pop_heap(bhp.begin(),bhp.end(),bcmp);//弹出大根堆顶点 
                    bhp.pop_back();
                    bcnt--;scnt++;
                    shp.push_back(t);//加入小根堆 
                    push_heap(shp.begin(),shp.end(),scmp);//维护小根堆(这个大概有用~) 
                }
            }
            else//拟加入小根堆 
            {
                scnt++;
                shp.push_back(num);
                push_heap(shp.begin(),shp.end(),scmp);
                if(scnt-bcnt>=2)//如果小根堆数据数过多 
                {
                    t=shp[0];
                    pop_heap(shp.begin(),shp.end(),scmp);//与上面相反! 
                    shp.pop_back();
                    scnt--;bcnt++;
                    bhp.push_back(t);
                    push_heap(bhp.begin(),bhp.end(),bcmp);
                }
            }
        }
        else//输出mid! 
        {
            if(n%2==0)
                cout<<min(shp[0],bhp[0])<<endl;
            else 
                cout<<shp[0]<<endl;
        }
    }
    return 0;
}

这里是题外话:

1.数据有点缺陷,如果初始数据只有一个数,堆创建会出现错误!( 但我A了 )
2.其实有很多方法比这个简单比如multiset,priority_queue等,但正好复习堆,我就用stl写了这( 偷懒QWQ )
最后一句话:
看提交记录会发现我错了10+遍,除了开始几遍,都是我垃圾队友瞎指点以至只得了20分
这里要提醒一下一定要以中位数为基准判定插入的新数应该放在哪个堆里,而不是根据插入的数据离哪个堆顶点更近...
还有一件事:不要轻易相信队友的话...
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值