zoukankan      html  css  js  c++  java
  • 对顶堆学习笔记

    (\)

    对顶堆


    处理动态中位数等问题,灵活运用了堆的性质,本质是维护两个堆。

    大根堆(Q_1):维护集合中较小值的部分的最大值。

    小根堆(Q_2):维护集合中较大值的部分的最小值。

    注意到两个堆中的元素各自是单调的,两个堆间也是单调的。也就是说,(Q_1)中的任何一个元素都不大于(Q_2)中的任何一个元素。

    那么假设高度为权值,两个堆可以形象化的表示成:

    如果两个堆的大小相差不超过(1),较大的那个堆的堆顶必定是中位数(偶数个数时中位数是排序后中间的两个之一)

    (UPD:) 图中的 (Q1)(Q2) 标反了,权值是越高越大。

    (\)

    具体操作


    • 插入

      先找到当前正确的集合插入,比较与堆顶的大小关系,大于小根堆的堆顶就插入大根堆,反之小根堆。

      调整两个堆的大小关系,因为两个堆不管是总体上还是各自内都是满足单调性的,所以每次取更大的堆的堆顶,插入另一个堆,直到两个堆的大小相差不超过(1)

      代码使用的是(STL)(priority\_queue),默认大根堆所以(Q_1)需要通过相反数实现。

      (!q2.size()||x>q2.top())?q1.push(-x):q2.push(x);
      while(q1.size()>q2.size()+1){q2.push(-q1.top());q1.pop();}
      while(q2.size()>q1.size()+1){q1.push(-q2.top());q2.pop();}
      
    • 查询

      直接输出比较大的堆的堆顶就好,注意(Q_1)的输出。

      if(i&1) printf("%d
      ",(q1.size()>q2.size())?-q1.top():q2.top());
      
    • 扩展

      动态(K)大值:维护思想不变,只需要让(Q_1)的大小为(K)即可。

      带删除(K)大值(/)中位数:手写可删堆(/)打标记懒惰删除法

    • 模板

      ([ POJ 3784 ] Running Median) 为例,只有插入操作,每插入到奇数个就输出当前中位数,多组数据。

      #include<cmath>
      #include<queue>
      #include<cstdio>
      #include<cctype>
      #include<cstdlib>
      #include<cstring>
      #include<iostream>
      #include<algorithm>
      #define N 100010
      #define R register
      #define gc getchar
      using namespace std;
      
      inline int rd(){
        int x=0; bool f=0; char c=gc();
        while(!isdigit(c)){if(c=='-')f=1;c=gc();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
        return f?-x:x;
      }
      
      int n,x,cnt;
      priority_queue<int> q1,q2,tmp;
      
      inline void work(){
        cnt=0; x=rd(); n=rd(); q1=tmp; q2=tmp;
        printf("%d %d
      ",x,(n+1)/2);
        for(R int i=1,x;i<=n;++i){
        	x=rd();
          (!q2.size()||x>q2.top())?q1.push(-x):q2.push(x);
          while(q1.size()>q2.size()+1){q2.push(-q1.top());q1.pop();}
          while(q2.size()>q1.size()+1){q1.push(-q2.top());q2.pop();}
          if(i&1){
            printf("%d ",(q1.size()>q2.size())?-q1.top():q2.top());
            if((++cnt)==10&&i!=n) cnt=0,puts("");
          }
        }
        puts("");
      }
      
      int main(){
        int t=rd();
        while(t--) work();
        return 0;
      }
      

    (\)

    一道例题


    对一个集合一共有(N)次操作,计数器(cnt)初始为(0​)

    • (ADD x):把(x)放进该集合。

    • (GET):将(cnt)(1),输出集合种中第(cnt)小的数。

    • (Nin [1,2 imes 10^5])

    (\)

    动态(K)小值,注意题目限制的(Q_2)大小是变化的,每次询问后记得调整好。

    (\)

    #include<cmath>
    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define N 200010
    #define R register
    #define gc getchar
    using namespace std;
    
    inline int rd(){
        int x=0; bool f=0; char c=gc();
        while(!isdigit(c)){if(c=='-')f=1;c=gc();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
        return f?-x:x;
    }
    
    int n,m,cnt,x,a[N];
    
    priority_queue<int> q1,q2;
    
    int main(){
        n=rd(); m=rd();
        for(R int i=1;i<=n;++i) a[i]=rd();
        x=rd();
        for(R int i=1;i<=n;++i){
            (!q2.size()||a[i]>q2.top())? q1.push(-a[i]):q2.push(a[i]);
            while(q2.size()>cnt) q1.push(-q2.top()),q2.pop();
            while(q2.size()<cnt) q2.push(-q1.top()),q1.pop();
            while(x==i){
                q2.push(-q1.top()); q1.pop();
                printf("%d
    ",q2.top()); 
                x=((++cnt)<m)?rd():-1;
            }
        }
        return 0;
    }
    
  • 相关阅读:
    jvm内存溢出问题
    表单提交后台正则表达式验证
    java项目改为web项目
    图片按像素压缩
    系统和压缩内存占用空间大
    teamview修改id
    新浪天气api
    SpringBoot+MySql+Mybatis+ thymeleaf 搭建个人博客
    FFmpeg的简单使用
    禁止f12及浏览器右键查看
  • 原文地址:https://www.cnblogs.com/SGCollin/p/9673252.html
Copyright © 2011-2022 走看看