LGOJ P1886 【滑动窗口】Solution

我偏要用STL!

这道题标签里给了线段树,单调队列等等多种解法。似乎很少使用STL的,用的比较多的也是deque,那么我来写一份利用另外一种结构定义的数据结构AC这道题的题解。

思路:
头指针指向i,尾指针指向i+k-1,先将1-k中的数字压入容器,求出两个最值,最小值当场输出,最大值存入数组。每次删去头指针指向的数,并执行i++,将新的尾指针指向的数压入容器,求最值,如上反复(对此不太理解的同学可以看一看“尺取法”)

注意:目前系列赛中多数已经允许使用O2优化,而使用O2优化一般情况下是STL能够被应用的基本条件之一。

数据结构:multiset

定义方式:

multiset<int,less<int> > q1;
  • 建立一个内部元素升序排列的multiset容器

multiset的容器定义于set内。但是multiset与set相比具有以下优点:

  • set中不能存储相同的元素,但是multiset可以。
  • multiset可以同时删除所有相同元素,也可以利用迭代器删除某一个位置的元素

相比于手写的单调队列,multiset有如下的优点:

  • 编程复杂度低,不需要实现复杂的过程,multiset自带insert(),find(),erase()等等好用的成员函数,不需要亲自实现
  • 在开启O2优化的情况下,multiset的运行速度并不比手动实现的数据结构要慢(multiset内部利用红黑树实现自我平衡,保证数据有序)

下面介绍几个需要用到的成员函数:

s.begin():返回一个指向容器头部的迭代器
  • 可以通过*s.begin()来取值。
s.end():返回一个指向容器尾部的迭代器
  • 可以通过*s.end()来取值。
  • 注意:s.end()存储的是一个被擦除的垃圾值,因此,真正的最大值在end的前一个迭代器内。我们可以将s.end()赋值给一个迭代器it,通过it–来实现.其中,it应该这样定义:
multiset<int>::iterator it;
s.find(int x):返回一个指向容器中第一个值为x的元素的迭代器
s.erase(iterator it):删除迭代器it指向的元素
  • 特别注明:erase中间如果传入整数x,那么会删除所有与x相同的元素,而本题每次删除一个,所以利用find和erase配合。
s.insert(int x):在容器中插入一个值为x的元素

注意细节:

  1. 输出时iterator必须使用*取值,因为iterator实际上就是一种复杂而特殊的指针
  2. 必须将s.end()赋值给it后再执行it–,然后输出*it,因为直接执行–s.end()可能会导致一些难以挽回的错误
  3. 为什么要先存储max值最后输出呢?为什么不能用两个multiset分别存储呢?因为这样可以节省空间和时间,在本题最大的测试点#9中,我的程序运行了980ms,消耗了28MB的空间。如果重新跑一遍取最大值肯定TLE(不要问我怎么知道的,看我记录就知道我TLE了多少次)
  4. 想TLE就尽管不用读入优化

代码实现:我知道你们最喜欢这个了

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
multiset <int,less<int> > q1;
inline int read(){
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
int n,m;
int dat[1001000];
int getmax[1001000];
int main()
{
    n=read();
    m=read();
    for(int i=1;i<=n;i++)
    {
        dat[i]=read();
    }
    for(int i=1;i<=m;i++)
    {
        q1.insert(dat[i]);
    }
    printf("%d ",*q1.begin());
    int j=1;
    multiset<int>::iterator it=q1.end();
    it--;
    getmax[j]=*it;
    for(int i=m+1;i<=n;i++)
    {
    	q1.insert(dat[i]);
    	q1.erase(q1.find(dat[i-m]));
    	printf("%d ",*q1.begin());
    	it=q1.end();
    	it--;
    	getmax[++j]=*(it);
    }
    printf("\n");
    for(int i=1;i<=j;i++)
    {
        printf("%d ",getmax[i]);
    }
    printf("\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值