单调栈
单调栈是一种特殊的栈,特殊之处在于栈内的元素都保持一个单调性。利用单调栈,可以找到从左/右遍历第一个比它小/大的元素的位置,利用O(n)的复杂度来处理一些看似复杂的问题
假如我们想找每个数右面的第一个比他严格大的数,暴力?当然可以,那有没有什么更好的方法,利用贪心的思想,从后往前计算,如果a[i]对应的f[i]已经算出,而且a[i]<=a[j],那么a[i]对f[1-j]一定没有影响,因为在1-(j-1)时与其取i的下标还不如取j的下标,所以在枚举的过程中找到一个比当前大的数就跳出,后面的数我们不管
这样对整个序列贪心,维护一个只保存有用元素的下标的栈,设栈顶为top,当前元素的下标为j 弹出栈顶的下标直到栈顶元素比当前元素大了(维护单调减的栈)就说明不能继续贪心了,记录更新答案,然后将当前下标入栈……(最靠前的可行的元素就是栈顶)
可以用来求寻找他的左面/右面第一个小于/大于他的数或者他的位置 POJ-3250
给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列的长度最大 POJ-2559 POJ 3494
给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大 POJ-2796
HDU-5033
while (top > 0 && s[top - 1] <= num) top--;//把破坏栈单调性的元素弹出
s[top++] = num;//下一个元素入栈
//此时栈中存的是元素的值
POJ-2559 Largest Rectangle in a Histogram
题意:在一条水平线上有n个宽为1的矩形,求包含于这些矩形的最大子矩形面积
要找最大的矩形的面积暴力一点的话,枚举每一个单位长度为1的矩形,分别向左右延伸,Smax=weight*hight复杂度O(n^2),我们的目的只是找到每个矩形向左右延伸的广度,就可以利用单调栈来做,这样的时间复杂度只有O(n)。本题压入栈中的是元素的值,但貌似压入编号更为常用,不过没关系,道理是一样的。
以题中样例为例:height =[2,1,5,6,2,3],
1) 刚开始栈中为空,压入2;然后当i=1时,2<=1不成立,下标出栈,栈变为空,计算此时面积res=2;
2)高度1,5,6是依次递增的,依次压入栈中;当i=4时,6>2,6出栈,所以计算此时的面积res=6,如图:
3)栈顶元素5, 5>2(此时,还要和下标i=4的高度比较),所以,编号为2的单位5出栈,计算面积为res=10;
4)因为栈顶元素为1,其对应的高度为1<2,满足条件,所以,将a[i]入栈,直到i=6(为压入的0);此时栈中的元素为{1,2,3},有一个栈顶元素对应的高度大于i=6对应的高度,所以,5出栈,计算面积为3,其中最大面积为10;
5)栈顶元素为2, 2>0,所以,2出栈,计算面积为4,最大面积为10;
6) 此时,栈顶元素为1, 1>0,所以,1出栈,因为1出栈以后,栈变为空,所以,计算面积的方式有变化,res=height[1]*6=6,最大面积为10。
可以维护一个单调递增的栈,用一个数组记录该高度能延伸的最长的距离,将之前比这高的矩形弹出栈直到遇到比新矩形低的矩形,同时累计他们的宽度之和,乘以这个更矮矩形的高度来更新答案。最后再按这个方法把所有矩形弹出去来更新答案,所以我们要在最结尾压入一个高度为0的单位,以保证栈中的所有元素都被弹出。
1 #include<stdio.h>
2 #include<algorithm>
3 using namespace std;
4 #define ll long long
5 const int inf=0x3f3f3f3f;
6 const int maxn=100009;
7 int a[maxn],z[maxn],num[maxn];
8 int main()
9 {
10 int n;
11 while(scanf("%d",&n),n)
12 {
13 for(int i=1;i<=n;i++)
14 {
15 scanf("%d",&a[i]);
16 }
17 a[n+1]=0;// 保证栈中的所有单位都被弹出
18 int top=0;//模拟栈
19 ll maxx=0;
20 for(int i=1;i<=n+1;i++)
21 {
22 ll tem=0;
23 while(top>0&&z[top]>=a[i])//将大于当前的高度弹出
24 {
25 tem+=num[top];//累计宽度
26 maxx=max(maxx,z[top]*tem);//更新Smax
27 top--;//弹出
28 }
29 z[++top]=a[i];//入栈
30 num[top]=tem+1;//要把弹出的单位的总宽度赋给下一个入栈的元素
31 //相当于把前面弹出的单位合并给下一个入栈的单位了??
32 }
33 printf("%lld
",maxx);
34 }
35 return 0;
36 }