• 单调栈 专题训练


    简介

    一、定义

    单调栈是一种高效,方便,简单的数据结构,其特点与单调队列类似,满足在栈里的数据呈现单调递增或者递减的特性,用来计算一定区间的值。

    二、原理

    (1)当新元素在单调性上优于栈顶时(单增栈新元素比栈顶大,单减栈新元素比栈顶小),压栈,栈深+1;

    (2)当新元素在单调性与栈顶相同(新元素于栈顶相同)或劣于栈顶时(单增栈新元素比栈顶小,单减栈新元素比栈顶大),弹栈,栈深-1;

    (3)根据题目条件计算栈变动后的最优值。

    三、实现形式

    STL的stack库,数组+栈顶指针

    四、几点注意

    1. 数据类型$long long$,建议$scanf$不要漏掉$lld$。
    2. 注意栈空的情况,此时无法取栈顶元素或者弹栈。
    3. 注意旧元素的延展性和新元素的继承性。

    例题

    洛谷 P3467 海报PLA

    基本思路

    当新元素与栈内某元素相同时,海报可以共用,因此海报数-1,。于是我们只需计算,若高度不同的时候,海报数+1,若相同,则海报数不变,其他遵守单调栈规则。

     1 #include<bits/stdc++.h>
     2 #define FOR(i,a,b) for(int i=a;i<b;i++)
     3 #define FOR2(i,a,b) for(int i=a;i<=b;i++)
     4 #define ll long long
     5 #define INF  0x7f7f7f7f;
     6 #define MAXN 2600100
     7 #define MOD 10007
     8 using namespace std;
     9 ll arr[MAXN];
    10 ll n,ans=0;
    11 stack<ll>s;
    12 int main()
    13 {
    14     cin>>n;
    15     FOR(i,0,n)
    16     {
    17         ll w;cin>>w>>arr[i];
    18         while(!s.empty()&&s.top()>=arr[i])
    19         {
    20             if(s.top()!=arr[i])
    21             {
    22                 ans++;
    23             }
    24             s.pop();
    25         }
    26         s.push(arr[i]);
    27     }
    28     cout<<ans+s.size()<<endl;
    29     return 0;
    30 }
    View Code

     洛谷 SP1805 HISTOGRA - Largest Rectangle in a Histogram

    基本思路

    每一列记录三个元素,当前高度$h$,左边扩展的最远的列数$l$,右边扩展最远的列数$r$。

    当一个新元素要入栈时,若新元素$h$>栈顶$h$,则栈内元素的右边界扩展到等于新元素所在列(站内元素$h$均小于新元素$h$);

    若新元素$h$<=栈顶$h$,则栈顶元素出栈,记录此时最大值,和栈顶元素的右边界$rr$;出栈后新的栈顶元素的右边界扩展到$rr$,计算此时最大值,直到栈顶元素$h$小于新元素$h$。

    举个栗子:n=7 , 2 1 4 5 1 3 3

    红色为即将弹出栈,深蓝色为已经出栈的元素,最后要插入一个虚矩形,使栈内元素全部出栈,计算最优值。

    AC代码如下

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<stack>
     4 #include<algorithm>
     5 #define FOR(i,a,b) for(int i=a;i<b;i++)
     6 #define FOR2(i,a,b) for(int i=a;i<=b;i++)
     7 #define ll long long
     8 #define INF  0x7f7f7f7f;
     9 #define MAXN 100100
    10 #define MOD 10007
    11 using namespace std;
    12 typedef struct{
    13     ll l,r,w;
    14 }NODE;NODE nodes[MAXN];
    15 ll arr[MAXN];
    16 ll n,ans=0;
    17 int main()
    18 {
    19     while(cin>>n){
    20     if(n==0)break;
    21     stack<NODE>s;;ans=0;
    22     FOR(i,0,n)
    23     {
    24         scanf("%lld",&nodes[i].w);
    25         nodes[i].l=nodes[i].r=i;
    26     }
    27     nodes[n]={n,n,0};
    28     FOR2(i,0,n)
    29     {
    30         while(!s.empty()&&s.top().w>=nodes[i].w)
    31         {
    32             ans=max(ans,s.top().w*(s.top().r-s.top().l+1));
    33             nodes[i].l=s.top().l;//新元素继承出栈元素的延展性 
    34             ll rr=s.top().r; 
    35             s.pop();
    36             if(!s.empty())
    37             {//仍在栈中的元素获得延展性,计算最大值 
    38                 s.top().r=rr;
    39             }
    40         }
    41         if(!s.empty())ans=max(ans,s.top().w*(s.top().r-s.top().l+1));
    42         s.push(nodes[i]);
    43     }
    44     printf("%lld
    ",ans);
    45     }
    46     
    47     return 0;
    48 }
    View Code

    相关应用

    最长上升子序列

    [洛谷P1823]音乐会的等待

    基本思路

    此题不需要考虑元素的延展性,只需要考虑身高相等的情况,记录连续相等的人数,并累加即可

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<stack>
     4 #include<algorithm>
     5 #define FOR(i,a,b) for(int i=a;i<b;i++)
     6 #define FOR2(i,a,b) for(int i=a;i<=b;i++)
     7 #define ll long long
     8 #define INF  0x7f7f7f7f;
     9 #define MAXN 500100
    10 #define MOD 10007
    11 using namespace std;
    12 typedef struct{
    13     ll w,sam;
    14 }NODE;NODE arr[MAXN];
    15 ll n,ans=0;
    16 stack<NODE>s;
    17 int main()
    18 {
    19     scanf("%lld",&n);
    20     FOR(i,0,n){
    21         scanf("%lld",&arr[i].w);arr[i].sam=1;//自己与自己相等 
    22         if(s.empty()){
    23             s.push(arr[i]);continue;
    24         }
    25         while(!s.empty()&&s.top().w<=arr[i].w)
    26         {
    27             if(s.top().w==arr[i].w)
    28             {//若相等则都能见面 
    29                 arr[i].sam+=s.top().sam;
    30             } 
    31             ans+=s.top().sam;
    32             s.pop();
    33         }
    34         if(!s.empty())ans++;
    35         s.push(arr[i]);
    36     }
    37     cout<<ans<<endl;
    38     return 0;
    39 }
    View Code

    [洛谷P4147&BZOJ 3039]玉蟾宫

    基本思路

    此题可用悬线法单调栈解决。

    若用单调栈,则对每一行进行处理,表示向上延伸的高度,即$a[i][j]=ch=='F'?a[i-1][j]+1:0;$

    注意末尾需要放置虚矩阵,然后逐行扫描,与SP1805 单调栈做法类似,考虑元素的延展性和单调性。

     1 #include<bits/stdc++.h>
     2 #include<iostream>
     3 #include<cstdio>
     4 #include<stack>
     5 #include<algorithm>
     6 #define FOR(i,a,b) for(int i=a;i<b;i++)
     7 #define FOR2(i,a,b) for(int i=a;i<=b;i++)
     8 #define ll long long
     9 #define INF  0x7f7f7f7f;
    10 #define MAXN 2000
    11 #define MOD 10007
    12 using namespace std;
    13 typedef struct{
    14     int w,h;
    15 }NODE;
    16 int n,m,arr[MAXN][MAXN];
    17 NODE dp[MAXN][MAXN];
    18 int main()
    19 {
    20 //    freopen("t1.in","r",stdin);
    21     cin>>n>>m;
    22     FOR2(i,1,n)
    23         FOR2(j,1,m)
    24         {
    25             char ch;
    26             cin>>ch;while(ch==' '||ch=='
    ')cin>>ch;
    27             dp[i][j].h=ch=='R'?0:dp[i-1][j].h+1;
    28         }
    29     FOR2(i,1,n)dp[i][m+1].h=0;
    30     int ans=0;
    31     FOR2(i,1,n)
    32     {
    33         stack<NODE>s;
    34         FOR2(j,1,m+1)
    35         {
    36             int temp=0;dp[i][j].w=1;
    37             while(!s.empty()&&s.top().h>=dp[i][j].h)
    38             {//使用temp记录比s.top().h小的元素通过延展可以获得的最大值 
    39                 temp+=s.top().w;
    40                 s.pop(); 
    41                 if(!s.empty())ans=max(ans,s.top().h*(temp+s.top().w));
    42             }
    43             s.push(dp[i][j]);
    44             s.top().w+=temp;//继承比dp[i][j]大的元素的延展性 
    45             ans=max(ans,s.top().h*s.top().w);
    46         }
    47     }
    48     cout<<ans*3<<endl;
    49     
    50     return 0;
    51 }
    View Code
  • 相关阅读:
    团队作业2:需求分析&原型设计
    第0次作业
    asp.net创建文件夹出错的解决方案[转]
    C# 线程手册 第一章 线程定义 线程
    Apache+php+mysql在windows下的安装与配置图解
    对于访问IIS元数据库失败的解决
    通过设置CSS position 属性来固定层的位置
    vs2008快捷键极其技巧 转载
    大文件上传组件
    C#代码备份还原MSSQL数据库
  • 原文地址:https://www.cnblogs.com/tldr/p/11302017.html
Copyright © 2020-2023  润新知