单调队列 POJ 2823+单调栈 HDU 1506

来源:互联网 发布:ug汽车大模具编程刀路 编辑:程序博客网 时间:2024/05/18 02:34

转自:http://blog.csdn.net/jane_jxr/article/details/68962534

单调队列就是用数组拉模拟队列的性质,保证队列里的元素是单调递增或单调递减,时间复杂度O(n)

    单调队列解决的是区间最小(最大)值。实现方法是:求区间最小(最大)值,就维护一个递增的双端队列,队中保存原始序列的标号,当即将入队的元素的值比队尾的元素的值小(大)的时候就不断弹掉队尾,知道出现比它更小的值,当即将入队的元素队首元素的跨度(即将入队元素的序号到队首元素序列的区间)大于规定区间时就不断弹掉队首,直到跨度小于或等于所规定的区间。如此可保证队首元素为最小(最大)值,(但不能保证队尾就是原始序列中的最大(最小)值),并维护区间长度。PS:借鉴别人的描述,大概就是这个道理

找区间最大最小值也能用RMQ,区间最值查询来写,但没有单调栈简单,复杂度低。

POJ 2823(单调队列):http://poj.org/problem?id=2823

eg:8 3(n个数,找区间范围为k的最大值和最小值)
    1 3 -1 -3 5 3 6 7
每次进出队列的是(数字,下标),下标是用来限制区间范围,删除队列有、头部无用的元素,下面来模拟这个过程
找一段区间的最小值,维护单调递增的队列
(1,1)
(1,1)(3,2)
(-1,3)
(-3,4)
(-3,4)(5,5)
(-3,4)(3,6)
(3,6)(6,7)//其中(-3,4)已经超过当前要找的区间范围了(7-3+1=5,当前要找的范围为【5,7】),属于无用元素,删除
(3,6)(5,7)(7,8)
找一段区间的最大值,原理是一样的,维护单调递减的队列,由于队列不能删除队尾元素,所以我们用数组来模拟的

自己可以手动模拟一边,这样才会更深的了解

#include<stdio.h>#include<stdlib.h>#include<iostream>#include<algorithm>#include<math.h>#include<map>#include<queue>#include<stack>#include<string.h>typedef long long LL;using namespace std;#define INF 0x3f3f3f3fconst int maxn=1000005;const double eps=0.0001;struct node{    int x,pos;} v[maxn];int mx[maxn],mn[maxn],n,m,a[maxn];void getmin()//维护单调递增的队列,找区间最小值{    int head=1,tail=0,i;    for(i=1; i<m; i++)    {        while(head<=tail&&v[tail].x>a[i])            tail--;        v[++tail].x=a[i],v[tail].pos=i;    }    for(; i<=n; i++)    {        while(head<=tail&&v[tail].x>a[i])            tail--;        v[++tail].x=a[i],v[tail].pos=i;        while(v[head].pos<i-m+1)            head++;        mn[i-m+1]=v[head].x;    }}void getmax()//维护单调递减的队列,找区间最大值{    int head=1,tail=0,i;    for(i=1; i<m; i++)    {        while(head<=tail&&v[tail].x<a[i])            tail--;        v[++tail].x=a[i],v[tail].pos=i;    }    for(; i<=n; i++)    {        while(head<=tail&&v[tail].x<a[i])            tail--;        v[++tail].x=a[i],v[tail].pos=i;        while(v[head].pos<i-m+1)            head++;        mx[i-m+1]=v[head].x;    }}int main(){    scanf("%d%d",&n,&m);    for(int i=1; i<=n; i++)        scanf("%d",&a[i]);    getmin();    for(int i=1; i<n-m+1; i++)        cout<<mn[i]<<" ";    cout<<mn[n-m+1]<<endl;    getmax();    for(int i=1; i<n-m+1; i++)        cout<<mx[i]<<" ";    cout<<mx[n-m+1]<<endl;}


HDU 1506:http://acm.hdu.edu.cn/showproblem.php?pid=1506
单调栈:顾名思义,就是单调递增或单调减的栈,跟单调队列差不多,但它只用到一端。时间复杂度O(n)
性质:1、单调栈里的元素具有单调性
           2、元素加入栈前,会在栈顶端把破坏栈单调性的元素都删除
           3、使用单调栈可以找到元素向左遍历第一个比他小的元素(单调递增的栈),也可以找到元素向左遍历第一个比他大的元素(单调递减的栈)。
       单调栈解决的是以某个值为最小(最大)值的最大区间。实现方法是:求最小值(最大值)的最大区间,维护一个递增(递减)的栈,当遇到一个比栈顶小的值的时候开始弹栈,弹栈停止的位置到这个值的区间即为此值左边的最大区间;同时,当一个值被弹掉的时候也就意味着比它更小(更大)的值来了,也可以计算被弹掉的值得右边的最大区间。PS:借鉴别人的描述,大概就是这个道理


#include<stdio.h>#include<stdlib.h>#include<iostream>#include<algorithm>#include<math.h>#include<map>#include<queue>#include<stack>#include<string.h>typedef long long LL;using namespace std;#define INF 0x3f3f3f3fconst int maxn=100005;const double eps=0.0001;struct node{    LL h,w;//h表示高度,w是不在栈里,但最小高度是h的有多少个,即:宽度} c[maxn];stack<node>s;int main(){    int n;    while(~scanf("%d",&n)&&n)    {        LL ans=0,cnt=0;        while(!s.empty())            s.pop();        c[0].h=0;        c[0].w=0;        s.push(c[0]);        for(int i=1; i<=n; i++)        {            scanf("%d",&c[i].h);            c[i].w=1;        }        for(int i=1; i<=n; i++)        {            if(c[i].h>s.top().h)            {                c[i].w=1;                s.push(c[i]);            }            else            {                cnt=0;                while(!s.empty()&&s.top().h>c[i].h)                {                    ans=max(ans,(cnt+s.top().w)*s.top().h);                    cnt+=s.top().w;//cnt表示要弹出的这个高度作用的范围(包括同一个栈中,已经弹出宽度的总和)                     //栈底的元素肯定能作用整个栈中所有高度的宽度总和(这点要好好理解),因为栈是单调递增的                    s.pop();                }                c[i].w=cnt+1;                s.push(c[i]);            }        }        cnt=0;        while(!s.empty())        {            ans=max(ans,(cnt+s.top().w)*s.top().h);            cnt+=s.top().w;            s.pop();        }        printf("%lld\n",ans);    }    return 0;}



0 0
原创粉丝点击