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;
    } 
     
  • 相关阅读:
    USACO Milk2 区间合并
    Codeforces 490B Queue【模拟】
    HDU 3974 Assign the task 简单搜索
    HDU 5119 Happy Matt Friends(2014北京区域赛现场赛H题 裸背包DP)
    Cin、Cout 加快效率方法
    POJ 1159 回文LCS滚动数组优化
    POJ 2479 不相交最大子段和
    POJ 1458 最长公共子序列 LCS
    在阿里最深刻的,还是职场之道给我的震撼
    精细化
  • 原文地址:https://www.cnblogs.com/akonoh/p/10216763.html
Copyright © 2011-2022 走看看