单调栈

//这是19号的话~拖了一天了
以后不能随便发pyp了//🙂
我们还是很水的,hduoj做到最后脑壳疼
现在还是先把今天看的单调栈做个小小的总结叭

今天先以HDU 1506为例

定义

单调递增或单调减的栈,跟单调队列差不多,但是只用到它的一端,利用它可以用来解决一些ACM/ICPC和OI的题目,如RQNOJ 的诺诺的队列等。

单调栈是一种特殊的栈,特殊之处在于栈内的元素都保持一个单调性。
假设下图是一个栈内元素的排列情况(单调递增的栈):


此时插入情况有两种:
(1).插入元素大于栈顶元素
当插入7时,因7 > 6,满足单调递增的条件,故可以直接加入栈
此时:


(2).插入的元素小于栈顶元素
当插入3时,为了满足单调递增栈的性质,需要先将栈顶的4,6弹出,再插入,此时:


结论

利用单调栈,可以找到从左/右遍历第一个比它小/大的元素的位置

栗子来啦:
假设有一个单调递增的栈 S和一组数列:
a : 5 3 7 4

用数组L[i] 表示 第i个数向左遍历的第一个比它小的元素的位置

如何求L[i]?

首先我们考虑一个朴素的算法,可以按顺序枚举每一个数,然后再依此向左遍历。
但是当数列单调递减时,复杂度是严格的O(n^2)。

此时我们便可以利用单调栈在O(n)的复杂度下实现

我们按顺序遍历数组,然后构造一个单调递增栈

(1). i = 1时,因栈为空,L[1] = 0,此时再将第一个元素的位置下标1存入栈中

此时栈中情况:


(2).i = 2时,因当前3小于栈顶元素对应的元素5,故将5弹出栈
此时栈为空
故L[2] = 0
然后将元素3对应的位置下标2存入栈中

此时栈中情况:


(3).i = 3时,因当前7大于栈顶元素对应的元素3,故
L[3] = S.top() = 2 (栈顶元素的值)

然后将元素7对应的下标3存入栈
此时栈中情况:


(4).i = 4时,为保持单调递增的性质,应将栈顶元素3弹出 (我认为他这里的3是说第三个也就是元素7,不然就不对了)
此时 L[4] = S.top() = 2;

然后将元素4对应的下标3存入栈
此时栈中情况:


对应的结果:
a : 5 3 7 4
L : 0 0 2 2

总结:

一个元素向左遍历的第一个比它小的数的位置就是将它插入单调栈时栈顶元素的值,若栈为空,则说明不存在这么一个数。然后将此元素的下标存入栈,就能类似迭代般地求解后面的元素

实现

1.最基础的应用就是给定一组数,针对每个数,寻找它和它右边第一个比它大的数之间有多少个数。

2.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列的长度最大。

3.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大。

代码模板

Stack<int> S;
    for(int i=1 ;i<=n ;i++){
        while(S.size() && a[S.top()] >= a[i]) S.pop();

        if(S.empty())     L[i] = 0;
        else              L[i] = S.top();

        S.push(i);
    }

例题:HDU 1506

题目链接:

首先考虑最大面积的矩形X的左右边界的性质:
设其左边界为L,右边界为R,则其高H = min{h[i] | L <= i <= R}
此时最大面积为 (R - L + 1) * H
若此时左边界的左边那个矩形的高度 h[L-1] >= H
则左边界可以向左拓展,则新的面积为:
(R - (L-1) + 1) * H > 原面积
则与原假设条件冲突
故左边界左边的那个矩形的高度 :h[L-1] < H
同理右边界右边的那个矩形的高度: h[R+1] < H
设H = h[i]
所以左边界L是满足h[j-1] < h[i]的最大的j,即从i点向左遍历的第一个高度比i小的点的右边一个点
而右边界R是满足 h[j+1] < h[i]的最小的j,即从i点向右遍历第一个高度比i小的点的左边一个点
所以我们可以利用单调栈的性质得到每个确定点,即确定高度的最大面积矩形的左右边界,然后枚举取最大即可。

现在最主要的问题是(R[i]-L[i])是对应下标为i的矩形的宽度,那R[i] 和 L[i]分别表示什么?

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;      //   这些做法可以学习
//typedef unsigned long long ull;

const int N= 100000+100;

stack<int>S;
ll h[N];        //这里的ll要小心,不然就会wrong
int R[N],L[N];
//L和R都是用来储存坐标,我要他从小到大排序,如果遇到了一个小的,就将之前比他大的全部出栈

int main()
{
    int n;
    freopen("data","r",stdin);
    while(cin>>n&&n>0){
        for(int i=0;i<n;i++)
                cin>>h[i];
        while(S.size()) S.pop();
        for(int i=0;i<n;i++){
            while(S.size()&&h[S.top()]>=h[i])
                S.pop();
            if(S.empty())   L[i]=0;            
            else    L[i]=S.top()+1;         
            S.push(i);
        }
        for(int i=0;i<n;i++)
            cout<<L[i];
        while(S.size()) S.pop();
        for(int i=n-1;i>=0;i--){
            while(S.size()&&h[S.top()]>=h[i])
                S.pop();
            if(S.empty())   R[i]=n;
            else    R[i]=S.top();           //为什么这个不用+1,因为i所对应的位置就是栈顶此刻对应的位置,无形中已经+1
            S.push(i);
        }
        for(int i=0;i<n;i++)
            cout<<R[i];
        ll ans=0;
        for(int i=0;i<n;i++)
            ans=max(ans,h[i]*(R[i]-L[i]));
        cout<<ans<<endl;
    }
    return 0;
}

关于模板我刚开始不理解这道题,就去借鉴了一下https://blog.csdn.net/zuzhiang/article/details/78134247 结果发现他对这道题的代码并不是很能理解,而且感觉他少了一边。然后又回来了这个代码

其实L和R记录的是元素i向左向右最长能到达的那个元素,可能再-1,这是开区间闭区间的区别。

假*单调栈

后来参考了一下师兄的简书,学会了一个假*单调栈
就拿这个例题的样例为例叭
input 2 1 4 5 1 3 3

先默认每个数的最左边就是左边的那个数,但是一旦遇到一个更小的数,它的左边界就不仅仅是左边最靠近它的那个数了,这时得循环,它可能是左边的左边界,甚至一直左边界下去,比如:L[5]=1,所以L[5]=L[L[5]],而L[5]其实就是下标4,向左移一位嘛,同理R的话是向右移一位。

代码如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define ll long long
using namespace std;
ll L[100001],R[100001],h[100001];

int main()
{   
    long long int n;
    freopen("data","r",stdin);
    while(scanf("%lld",&n)&&n){
        for(int i=1;i<=n;i++)
            scanf("%lld",&h[i]);
        for(int i=1;i<=n;i++){
            L[i]=i-1;
            R[i]=i+1;
        }
        for(int i=1;i<=n;i++)
            while(L[i]&&h[L[i]]>=h[i])
                L[i]=L[L[i]];
        for(int i=n;i>0;i--)
            while(R[i]<=n&&h[R[i]]>=h[i])
                R[i]=R[R[i]];
        long long int sum=0;
        for(int i=1;i<=n;i++)
            sum=sum>(h[i]*(R[i]-L[i]-1))?sum:(h[i]*(R[i]-L[i]-1));
        printf("%lld\n",sum);
    }
    return 0;
}

义无反顾入坑

  • n,h,R,L要是long long 型
  • sum=sum>(h[i](R[i]-L[i]-1))?sum:(h[i](R[i]-L[i]-1));这个如果不确定的话可以自己去瞎掰模拟一下//虽然会浪费时间
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,517评论 0 38
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,538评论 0 13
  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,313评论 0 5
  • 1)这本书为什么值得看: Python语言描述,如果学的Python用这本书学数据结构更合适 2016年出版,内容...
    孙怀阔阅读 12,286评论 0 15
  • 总以为我是一个比较冷淡冷情的人,于爱情不会有太大的期许,平平淡淡,稳稳妥妥,但是看到他来火车站接我,我竟然会如此高...
    Likesunshine阅读 302评论 0 1