单调栈

陌路散爱 提交于 2019-11-27 08:34:47

 单调栈:

单调栈解决的是:以某个值为最小(最大)值的最大区间。

实现方法是:求最小值(最大值)的最大区间,维护一个递增(递减)的栈,当遇到一个比栈顶小的值的时候开始弹栈,弹栈停止的位置到这个值的区间即为此值左边的最大区间;同时,当一个值被弹掉的时候也就意味着比它更小(更大)的值来了,也可以计算被弹掉的值的右边的最大区间。

单调递增:数据出栈的序列为单调递增序列(即从栈顶到栈底的元素是单调递增的)

单调递减:数据出栈的序列为单调递减序列(与上面相反)

 模板如下:

 1 stack<int> sta;
 2 for (遍历这个数组)
 3 {
 4     while(栈不为空 && 栈顶元素小于当前元素){
 5         更新结果;
 6         栈顶元素出栈;
 7     }
 8     if(栈空 || 栈顶元素大于等于当前比较元素){
 9         当前数据入栈;
10     }
11 }

 或

 1 stack<int> sta;
 2 for (遍历这个数组)
 3 {
 4     if(栈空 || 栈顶元素大于等于当前比较元素){
 5         入栈;
 6     }
 7     else{
 8         while(栈不为空 && 栈顶元素小于当前元素){
 9             更新结果;
10             栈顶元素出栈;
11         }
12         当前数据入栈;
13     }
14 }
15                             

 /*如果要使最后单调栈内的元素能全部弹出,一般要在数组最后加一个符合条件的值,使栈内元素能全部弹出。*/

例题:Largest Rectangle in a Histogram

题意

求所给出的条形图可形成的最大矩形面积。

题解

以每一个矩形的高度作为最终大矩形的高度,看最宽能是多少,然后统计最优解。(暴力法O(n^2)超时)

维护单调栈(单调递减),当前高度数值大于栈顶元素大小则入栈,小于则出栈。在维护单调性的弹出操作时统计宽度,不断更新答案即可得到最优解。

Code

STL的stack:

 1 #include<cstdio>
 2 #include<stack>
 3 using namespace std;
 4 typedef long long ll;
 5 const int maxn=1e5+5;
 6 ll h[maxn];
 7 int main()
 8 {
 9     ll n;
10     while(~scanf("%lld",&n)&&n){
11         for(int i=1;i<=n;i++){
12             scanf("%lld",&h[i]);
13         }
14         h[n+1]=0;//为了让栈内的元素最后能全部弹出,令h[n+1]=0。因为是多测试用例,所以这里必须重新初始化,否则可能会受前一次测试用例影响
15         ll area,Max=-1;
16         stack<pair<ll,ll> >S;//第一个存高度,第二个存当前高度可达到的左端
17         for(int i=1;i<=n+1;i++){
18             ll L=i;
19             while(!S.empty()&&h[i]<=S.top().first){ //出栈
20                 L=S.top().second;
21                 area=(i-L)*S.top().first;//L为矩形左边界,当前i为右边界,i-L为矩形的宽,S.top().first为矩形的高
22                 Max=max(area,Max);
23                 S.pop();
24             }
25             if(S.empty()||h[i]>S.top().first)//入栈
26                 S.push({h[i],L});
27         }
28         printf("%lld\n",Max);
29     }
30     return 0;
31 }

 手写栈:

 1 /*AC 109ms*/
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 typedef long long ll;
 6 const int MAX=1e5+5;
 7 using namespace std;
 8 ll h[MAX],w[MAX],stack[MAX];
 9 int main()
10 {
11     int n;
12     while(scanf("%d",&n)&&n)
13     {
14         memset(h,0,sizeof(h));//多测试用例,必须要重新初始化
15         ll ans=0,k;///k为右宽
16         for(int i=1;i<=n;i++){
17             scanf("%lld",&h[i]);
18         }
19         
20         h[n+1]=0;//为了方便把最后剩下的单调递增的矩形也统计进去,我们假设h[n+1]的位置有一个高度为0的矩形,最后将它加入单调栈时他会将所有矩形都弹出,那么答案也就完成最后的更新了。
21         int top=0;
22         stack[0]=-1;///注意这里要将单调栈的0下标位置初始化为-1,否则有可能会在下面while处陷入死循环导致超时
23         
24         for(int i=1;i<=n+1;i++){
25             if(h[i]>stack[top]){///严格单调递减
26                 stack[++top]=h[i];///入栈
27                 w[top]=1;//左宽为1,即该入栈元素本身
28             }
29             else{
30                 k=0;///第一个出栈元素右宽为0
31                 while(h[i]<=stack[top]){
32                     w[top]+=k;///算出该出栈元素的总宽(左宽+右宽)
33                     ans=max(stack[top]*w[top],ans);///计算以该出栈元素为高的矩形面积,更新最优解
34                     k=w[top];///下一个出栈元素的右宽为上一个出栈元素的总宽
35                     top--;
36                 }
37                 stack[++top]=h[i];///入栈
38                 w[top]=1+k;//该入栈元素左宽为最后一个出栈元素的总宽+1
39             }
40         }
41         printf("%lld\n",ans);
42     }
43     return 0;
44 }

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!