单调队列

问题引入-POJ2823

Description

An array of size n ≤ 10 6 is given to you. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example: The array is [1 3 -1 -3 5 3 6 7], and k is 3.
Window positionMinimum valueMaximum value
[1  3  -1] -3  5  3  6  7 -13
1 [3  -1  -3] 5  3  6  7 -33
1  3 [-1  -3  5] 3  6  7 -35
1  3  -1 [-3  5  3] 6  7 -35
1  3  -1  -3 [5  3  6] 7 36
1  3  -1  -3  5 [3  6  7]37

Your task is to determine the maximum and minimum values in the sliding window at each position.

Input

The input consists of two lines. The first line contains two integers n and k which are the lengths of the array and the sliding window. There are n integers in the second line.

Output

There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values.

Sample Input

8 3
1 3 -1 -3 5 3 6 7

Sample Output

-1 -3 -3 -3 3 3
3 3 5 5 6 7

题意分析:给定一个数列,从左到右输出每个长度为m的数列段内的最小数和最大数,有一个滑动区间,每次我们找到这个区间的最大值和最小值

解法:从数列的开头,将窗放上去,然后找到这最开始的k个数的最大值,然后窗最后移一个单元,继续找到k个数中的最大值。这种方法每求一个f(i),都要进行k-1次的比较,复杂度为O(Nk)。如果暴力时间复杂度为O(Nm)会超时在暴力枚举的过程中,有一个地方是重复比较了,就是在找当前的f(i)的时候,i的前面其它m-1个数在算f(i-1)的时候我们就比较过了。当你一个个往下找时,每一次都是少一个然后多一个,如果少的不是最大值,然后再问新加进来的,看起来很省时间对吧,那么如果少了的是最大值呢?第二个最大值是什么?那么我们能不能保存上一次的结果呢?当然主要是i的前k-1个数中的最大值了。答案是可以,这就要用到单调队列。

单调队列定义:

 

  • 1、维护区间最值
  • 2、去除冗杂状态 如上题,区间中的两个元素a[i],a[j](假设现在再求最大值)
    若 j>i且a[j]>=a[i] ,a[j]比a[i]还大而且还在后面(核心思想:目前a[j]留在队列肯定比a[i]有用,因为你是往后推!)
  • 3、保持队列单调,最大值是单调递减序列,最小值反之
  • 4、最优选择在队首

 单调队列的维护过程:

  1.维护队首(对于上题就是如果队首已经是当前元素的m个之前,则队首就应该被删了,head++)
  2.在队尾插入(每插入一个就要从队尾开始往前去除冗杂状态,保持单调性)

https://blog.csdn.net/er111er/article/details/78344161

简单举例应用
数列为:6 4 10 10 8 6 4 2 12 14
N=10,K=3;
那么我们构造一个长度为3的单调递减队列:
首先,那6和它的位置0放入队列中,我们用(6,0)表示,每一步插入元素时队列中的元素如下
插入6:(6,0);
插入4:(6,0),(4,1);
插入10:(10,2);
插入第二个10,保留后面那个:(10,3);
插入8:(10,3),(8,4);
插入6:(10,3),(8,4),(6,5);
插入4,之前的10已经超出范围所以排掉:(8,4),(6,5),(4,6);
插入2,同理:(6,5),(4,6),(2,7);
插入12:(12,8);
插入14:(14,9);
那么f(i)就是第i步时队列当中的首元素:6,6,10,10,10,10,8,6,12,14
同理,最小值也可以用单调队列来做。

单调队列的时间复杂度是O(N),因为每个数只会进队和出队一次,所以这个算法的效率还是很高的。
注意:建议直接用数组模拟单调队列,因为系统自带容器不方便而且不易调试,同时,每个数只会进去一次,所以,数组绝对不会爆,空间也是S(N),优于堆或线段树等数据结构。

单调队列有单调递增和单调递减两种,一般来讲,队列的队首是整个队列的最大值或最小值

单调队列可以解决许多问题,而且可以用来优化DP:

  单调队列的实现:(双端队列,一段入,两端输出)

    具体步骤:

      1.   若队列为空,将A[i]从队尾入队

      2.  若队列不为空,将比A[i]大的元素都从队尾弹出,然后把A[i]入队

      3.  若队列不为空且A[i]大于队尾,则直接从队尾把A[i]入队

    实现一般采用双端队列:

复制代码
 1 if(q.empty())
 2   q.push_back(A[i]);
 3 else if(q.back()>A[i]){  4 while((!q.empty())&&q.back()>A[i]){  5  q.pop_back();  6  }  7  q.push_back(A[i]);  8 }  9 else 10 q.push_back(A[i]);
复制代码

单调队列有许多作用:

比如可以求出一个数组内第一个大于等于一个数x的数,也可以通过维护单调性,解决一些区间内最小或最大的问题,总之单调队列的应用在根本上要视题目而定的灵活运用。

单调队列是DP优化的一种,能实现O(VN)的多重背包。单调队列是用一个单调的队列来储存必要的元素,并不储存无用的元素。

单调是一种思想,当我们解决问题的时候发现有许多冗杂无用的状态时,我们可以采用单调思想,用单调队列或类似于单调队列的方法去除冗杂状态,保存我们想要的状态.

Poj 2823代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
    int x,y;
}v[1010000]; //x表示值,y表示位置 可以理解为下标
int a[1010000],n,m,mx[1010000],mn[1010000];
void getmin()
{
    int i,head=1,tail=0;// 默认起始位置为1 因为插入是v[++tail]故初始化为0
    for(i=1;i<m;i++)
    {
        while(head<=tail && v[tail].x>=a[i]) tail--;
        v[++tail].x=a[i],v[tail].y=i;
                // 根据题目 前m-1个先直接进入队列
    }
    for(;i<=n;i++)
    {
        while(head<=tail && v[tail].x>=a[i]) tail--;
        v[++tail].x=a[i],v[tail].y=i;
        while(v[head].y<i-m+1) head++;
        mn[i-m+1]=v[head].x;
               // 道理同上,当然了 要把已经超出范围的从head开始排出
               //  然后每个队首则是目前m个数的最小值
    }
}
void getmax() //最大值同最小值的道理,只不过是维护的是递减队列
{
    int i,head=1,tail=0;
    for(i=1;i<m;i++)
    {
        while(head<=tail && v[tail].x<=a[i]) tail--;
        v[++tail].x=a[i],v[tail].y=i;
    }
    for(;i<=n;i++)
    {
        while(head<=tail && v[tail].x<=a[i]) tail--;
        v[++tail].x=a[i],v[tail].y=i;
        while(v[head].y<i-m+1) head++;
        mx[i-m+1]=v[head].x;
    }
}
int main()
{
    int i,j;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    getmin();
    getmax();
    for(i=1;i<=n-m+1;i++)
    {
        if(i==1)printf("%d",mn[i]);
        else printf(" %d",mn[i]);
    }
    printf("\n");
    for(i=1;i<=n-m+1;i++)
    {
        if(i==1)printf("%d",mx[i]);
        else printf(" %d",mx[i]);
    }
    printf("\n");
    return 0;
}

 单调队列例题:

Leetcode 300:最长上升子序列

https://www.cnblogs.com/yuiffy/p/3885832.html

转载于:https://www.cnblogs.com/zwjjj/p/10462513.html

内容概要:本文详细介绍了QY20B型汽车起重机液压系统的设计过程,涵盖其背景、发展史、主要运动机构及其液压回路设计。文章首先概述了汽车起重机的分类和发展历程,强调了液压技术在现代起重机中的重要性。接着,文章深入分析了QY20B型汽车起重机的五大主要运动机构(支腿、回转、伸缩、变幅、起升)的工作原理及相应的液压回路设计。每个回路的设计均考虑了性能要求、功能实现及工作原理,确保系统稳定可靠。此外,文章还详细计算了支腿油缸的受力、液压元件的选择及液压系统的性能验算,确保设计的可行性和安全性。 适合人群:从事工程机械设计、液压系统设计及相关领域的工程师和技术人员,以及对起重机技术感兴趣的高等院校学生和研究人员。 使用场景及目标:①为从事汽车起重机液压系统设计的工程师提供详细的参考案例;②帮助技术人员理解和掌握液压系统设计的关键技术和计算方法;③为高等院校学生提供学习和研究起重机液压系统设计的实用资料。 其他说明:本文不仅提供了详细的液压系统设计过程,还结合了实际工程应用,确保设计的实用性和可靠性。文中引用了大量参考文献,确保设计依据的科学性和权威性。阅读本文有助于读者深入了解汽车起重机液压系统的设计原理和实现方法,为实际工程应用提供有力支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值