zoukankan      html  css  js  c++  java
  • 数据结构番外篇【stl应用(1)】优先队列

    stl是一种重要技巧,可以极大地简化编程过程
    在总结stl之前,我们先简单介绍一下迭代器。
    迭代器可以简单理解为地址的等价物。
    在不同数据类型中迭代器支持的操作略有不同
    官方文档中的定义
    其中vector使用的是随机访问迭代器,其支持的操作可以参考上述表格
    虽然本文用不上预备知识,但是还是先说一下吧
    接下来介绍常用stl——优先队列

    1.优先队列

    优先队列(英文priority_queue)是一种维护集合最大最小值的数据结构(堆)
    它能够在O(1)时间得到一个集合的最大(小)值,在O(logn)时间加入新的元素或者删除最大(小)的元素
    常见操作如下:
    优先队列默认为大根堆,即维护最大值

    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <iostream>
    using namespace std;
    priority_queue<int> q1;//默认大根堆
    priority_queue<int,vector<int>,less<int> > q2;//等价的大根堆
    priority_queue<int,vector<int>,greater<int> > q3;//小根堆
    int main()
    {
    	int n;
    	scanf("%d",&n);
    	for(int i = 1; i <= n; i ++)
    	{
    		int x;scanf("%d",&x);
    		q1.push(x);
    		q2.push(x);
    		q3.push(x);//插入新元素
    	}
    	while(!q1.empty())//检查是否非空
    	{
    		printf("%d\n",q1.top());//返回最大(小)值
    		q1.pop();//删除最大(小)值
    	}
    	while(q3.size()>0)//返回元素个数
    	{
    		printf("%d\n",q3.top());
    		q3.pop();
    	}
    }

    优先队列的优化十分优秀,很多情况下甚至比手写heap还要快

    例题

    洛谷P3871 中位数
    题目描述
    给定一个由N个元素组成的整数序列,现在有两种操作:
    1 add a
    在该序列的最后添加一个整数a,组成长度为N + 1的整数序列
    2 mid
    输出当前序列的中位数
    中位数是指将一个序列按照从小到大排序后处在中间位置的数。(若序列长度为偶数,则指处在中间位置的两个数中较小的那个)
    例1:1 2 13 14 15 16 中位数为13
    例2:1 3 5 7 10 11 17 中位数为7
    例3:1 1 1 2 3 中位数为1
    输入输出格式
    输入格式:

    第一行为初始序列长度N。第二行为N个整数,表示整数序列,数字之间用空格分隔。第三行为操作数M,即要进行M次操作。下面为M行,每行输入格式如题意所述。
    输出格式:

    对于每个mid操作输出中位数的值
    https://www.luogu.org/problemnew/show/P3871
    这道题有很多做法,在此给出简单且时间效率高的优先队列做法
    将数组按照从小到大排序,将较小的一半放入一个大根堆,将较大的一半放入一个小根堆
    每次添加数的时候与两个堆堆顶元素比较,将其放入相应的堆中
    确保左堆的元素个数>=右堆的元素个数>=左堆元素个数-1
    若打破了这个条件,只需将某堆中的堆顶元素换到另一个堆即可
    每一次询问输出左堆堆顶

    #include <iostream>
    #include <algorithm>
    #include <set>
    #include <cstdio>
    #include <vector>
    #include <queue>
    using namespace std;
    priority_queue <int,vector<int>,greater<int>> qright;
    priority_queue <int,vector<int>,less<int>> qleft; 
    vector <int> v;
    int main() 
    {
        int n,m;
        scanf("%d",&n);
        v.push_back(0);
        for(int i = 1; i <= n ;i++)
        {
            int x;
            scanf("%d",&x);
            v.push_back(x);
        }
        sort(v.begin()+1,v.end());
        for(int i = 1; i <= n/2; i ++)qleft.push(v[i]);
        for(int i = n/2+1; i <= n; i ++)qright.push(v[i]);
        scanf("%d",&m);
        while(m--)
        {
            char str[10];
            scanf("%s",str);
            if(str[0]=='m')
            {
                printf("%d\n",qleft.top());
            }
            else
            {
                int x;
                scanf("%d",&x);
                if(x<=qleft.top())
                {
                    qleft.push(x);
                }
                else qright.push(x);
                int a = qleft.size(),b=qright.size();
                if(a-b>1)
                {
                    qright.push(qleft.top());
                    qleft.pop();
                }
                else if(b>a)
                {
                    qleft.push(qright.top());
                    qright.pop();
                }
            }
        }
    }

    https://www.luogu.org/problemnew/show/P1484
    洛谷P1484 种树
    题目描述
    cyrcyr今天在种树,他在一条直线上挖了n个坑。这n个坑都可以种树,但为了保证每一棵树都有充足的养料,cyrcyr不会在相邻的两个坑中种树。而且由于cyrcyr的树种不够,他至多会种k棵树。假设cyrcyr有某种神能力,能预知自己在某个坑种树的获利会是多少(可能为负),请你帮助他计算出他的最大获利。
    输入输出格式
    输入格式:

    第一行,两个正整数n,k。
    第二行,n个正整数,第i个数表示在直线上从左往右数第i个坑种树的获利。
    输出格式:

    输出1个数,表示cyrcyr种树的最大获利。
    这道题采用的思想是在一棵树被种之后,要改种左右两颗树获利等于左右两树之和减去这棵树,我们把这个获利再当成之前被种的树的获利放入堆中,并且标记左右两棵树无效。
    最后k次取堆顶元素求和

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    using namespace std;
    #define N 500004
    int f[N],l[N],r[N],n,k,cnt,tot;
    long long ans;
    bool vis[N<<2];
    struct num
    {
     int pos,val;
     bool operator < (const num b)const
     {
      return val<b.val;
     } 
    }buf; 
    priority_queue <num> heap;
    int main()
    {
     scanf("%d%d",&n,&k);
     for(int i = 1; i <= n; i ++)
     {
      scanf("%d",f+i);
      heap.push((num){i,f[i]});
      vis[i]=1;
      r[i]=i+1;
      l[i]=i-1;
     }
     for(int i = 1; i <= k ; i ++)
     {
      while(!heap.empty()&&!vis[heap.top().pos])heap.pop();
      if(heap.empty())break;
      buf=heap.top();
      heap.pop();
      vis[r[buf.pos]]=vis[l[buf.pos]]=0;
      if(buf.val<0)break;
      ans+=buf.val;
      heap.push((num){buf.pos,f[buf.pos]=f[r[buf.pos]]+f[l[buf.pos]]-buf.val});
      r[buf.pos]=r[r[buf.pos]];l[r[buf.pos]]=buf.pos;
      l[buf.pos]=l[l[buf.pos]];r[l[buf.pos]]=buf.pos;
      vis[buf.pos]=1;
     }
     cout<<ans;
    } 
     
  • 相关阅读:
    【复现】cve-2020-1472 微软NetLogon权限提升漏洞 附exp
    Vmware 问题【持续更新】
    linux命令之上传文件和下载文件-lrzsz
    在ubuntu16.04中输入不存在shell命令时,报错ModuleNotFoundError的解决方案
    linux编译命令:tmpfs,make,distcc,ccache
    Linux常用命令之必知必会
    crontab定时任务
    Ubuntu使用官网的安装包安装Nodejs
    接口测试(Python)之DDT
    【XMCTF】web3 考核
  • 原文地址:https://www.cnblogs.com/akonoh/p/10216763.html
Copyright © 2011-2022 走看看