单调队列+滑动窗口例题题解

本文为博主原创文章,未经允许不得转载。如有问题,欢迎指正!
本文链接:https://blog.csdn.net/IAMLSL/article/details/107420709

目录

 

单调队列简介

模板题: 洛谷   P1886 滑动窗口

用双端队列deque实现用单调队列求区间最大值(核心过程即代码模板):

完整代码:

做题小结:


单调队列简介

单调队列适用于快速求出一段区间最大值(最小值)的情况,以下讨论求最大值的问题:

在我看来,思路就是维护一个单调递减的队列:往队尾插入元素时,比较插入元素和队尾元素,依次删除所有比插入元素大的队尾元素,直到有元素大于插入元素或者队列为空时,将插入元素入队。队首元素就是当前区间的最大值,若对区间长度有要求,则在队首去掉已经不在区间里的元素。

模板题: 洛谷   P1886 滑动窗口

题目地址:https://www.luogu.com.cn/problem/P1886

题目详解:

因为求最大值和最小值的思想类似,故此处以求区间里的最大值为例:

依据前面对优先队列的描述,不难发现优先队列的实现中要在队尾插入元素,在队首或队尾删除元素。于是可以考虑用C++stl

中的双端队列deque来模拟整个过程。

用双端队列deque实现用单调队列求区间最大值(核心过程即代码模板):

struct elem{
 int id;       //下标
 int value;    //元素值
};
//定义结构体elem记录元素值和其对应的下标(下标值用于判断元素是否在当前区间)


vector<elem>V;
//定义vector<elem>V来存储所有元素值


deque<elem>maX;
//定义双端队列maX存储并更新维护这个单调递减的队列(单调队列)

 

//具体过程:
//遍历V中元素,在插入元素前除大于当前元素的队尾元素和不在当前区间的队首元素出队
  for(int i=0;i<n;i++){
         elem now=V[i];
  
     //去除大于当前元素的队尾元素
         while(!maX.empty()&&(now.value>=maX.back().value ) ){
         maX.pop_back();
         }


     //已经不在当前区间的队首元素出队:
        while(!maX.empty()&&(now.id-maX.front().id)>=a){
         maX.pop_front();
         }
  
     
        //将当前元素入队,并输出最大值
         maX.push_back(now);
         if(i+1>=a){
         write(maX.front().value);
         printf(" ");
           }
        }   

完整代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<deque>
   using namespace std;
    struct elem{
    	int id;
    	int value;
	};

   int read() {
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {
        if(ch=='-') f=-f;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}

  void write(int x) {
    if(x<0) {
        putchar('-');
        x=-x;
    }
    if(x>9) write(x/10);
    putchar(x%10+'0');
}

	 int main(){
	 vector<elem>V;	
	 deque<elem>miN;
	 deque<elem>maX;
	 int n,a;
	 n=read();
	 a=read();
	    for(int i=0;i<n;i++){
	 	int v;
		v=read();
	    elem va;
	    va.id =i;
	    va.value=v;
		V.push_back(va);  
		 }
        for(int i=0;i<n;i++){
	  elem now=V[i];
	  while(!miN.empty()&&(now.value<=miN.back().value )){
          miN.pop_back();
		 }
	    while(!miN.empty()&&(now.id-miN.front().id)>=a){
		 miN.pop_front();
		 }
		  miN.push_back(now);
		 if(i+1>=a){
		 write(miN.front().value);
		 printf(" ");
	      }
           } 
        cout<<endl;
     

	for(int i=0;i<n;i++){
	   elem now=V[i];
	   while(!maX.empty()&&(now.value>=maX.back().value ) ){
	   maX.pop_back();
		 }
	    while(!maX.empty()&&(now.id-maX.front().id)>=a){
		 maX.pop_front();
		 }
		 maX.push_back(now);
	     if(i+1>=a){
	     write(maX.front().value);
	     printf(" ");
	       }
                 }    
          return 0;   
        }
        
        

[点击并拖拽以移动]
​

做题小结:

1.此题用C++的输入输出最后一组数据超时,copy来快速读入和输出的函数read()和write()函数就轻松AC了。

2.可以用back()函数获取deque的队尾元素

3.单调队列具体操作总结为:遍历元素,在插入元素前除大于当前元素的队尾元素和不在当前区间的队首元素出队。

4. 调试心得:某一次将maX.back()赋值给变量first代如while循环中,写成下面这样:

elem first=maX.back();

while(!maX.empty()&&(now.value>=first.value ) )     maX.pop_back();

在循环中尽量不用“中间变量”去替代一些会变化的值!如此处maX.back()就是变化的。

正确写法:   while(!maX.empty()&&(now.value>=maX.back().value ) )

猜你喜欢

转载自blog.csdn.net/IAMLSL/article/details/107420709