单调栈
单调栈是一种特殊的栈,特殊之处在于栈内的元素都保持一个单调性,可能为单调递增,也可能为单调递减。
性质:
单调栈里的元素具有单调性
元素加入栈前,会在栈顶端把破坏栈单调性的元素都删除
使用单调栈可以找到元素向左遍历第一个比他小的元素,也可以找到元素向左遍历第一个比他大的元素。
单调栈的维护是 O(n) 级的时间复杂度,因为所有元素只会进入栈一次,并且出栈后再也不会进栈了。
由于单调栈只能对栈顶位置进行操作 所以一般应用于只有对数组有一边有限制的地方
实现用STL的栈实现即可 模拟栈也行
初步来总结一下单调栈吧,单调栈其实是一个看似原理简单,但是可以变得很难的解法。线性的时间复杂度是其最大的优势,每个数字只进栈并处理一次,而解决问题的核心就在处理这块,当前数字如果破坏了单调性,就会触发处理栈顶元素的操作,而触发数字有时候是解决问题的一部分,比如在 Trapping Rain Water 中作为右边界。有时候仅仅触发作用,比如在 Largest Rectangle in Histogram 中是为了开始处理栈顶元素,如果仅作为触发,可能还需要在数组末尾增加了一个专门用于触发的数字。另外需要注意的是,虽然是递增或递减栈,但里面实际存的数字并不一定是递增或递减的,因为我们可以存坐标,而这些坐标带入数组中才会得到递增或递减的数。所以对于玩数组的题,如果相互之间关联很大,那么就可以考虑考虑单调栈能否解题。
单调队列
性质:
单调队列和单调栈很像,就是一个维护了单调性的队列数据结构,可以是单调递增的,也可以是单调递减的。
单调队列满足两个性质
1.单调队列必须满足从队头到队尾的严格单调性。
2.排在队列前面的比排在队列后面的要先进队。
元素进队列的过程对于单调递增队列,对于一个元素a 如果 a > 队尾元素 那么直接将a扔进队列 如果 a <= 队尾元素 则将队尾元素出队列 知道满足 a 大于队尾元素即可;
实现用 stl 的双端队列即可。
由于双端队列即可以在队头操作也可以在队尾操作那么这样的性质就弥补了单调栈只能在一遍操作的不足可以使得其左边也有一定的限制。
https://blog.csdn.net/zhelong3205/article/details/77624699
https://blog.csdn.net/u011893609/article/details/78806089
https://www.cnblogs.com/tham/p/8038828.html
https://blog.csdn.net/qq_30578131/article/details/80740614
没什么好说的,直接上题
提示:点击题目直达题目提交地址
Largest Rectangle in a Histogram
大致题意:
给定从左到右多个矩形,已知这此矩形的宽度都为1,长度不完全相等。
这些矩形相连排成一排,求在这些矩形包括的范围内能得到的面积最大的矩形,输出该面积。
所求矩形可以横跨多个矩形,但不能超出原有矩形所确定的范围。
算法分析&&具体操作:
借助单调性处理问题的思想在于及时排除不可能的选项,保持策略集合的高度有效性和秩序性
对于此题,我们需要从左向右求出每个矩形向右能够扩展到的最大连续宽度
能够扩展的条件:矩形高度大于等于当前矩形高度
那么我们求完之后算出面积取 max 即可
显然朴素算法是不行的,我们需要用到单调栈
准确的说是:
单调栈维护从起点矩形到当前矩形的高度递增序列
如果当前矩形低于于栈顶矩形,我们就一直将栈中元素弹出,直到遇到低于当前点的矩形,以此来维护栈的递增性,显然此时的点最远可以扩展到当前栈元素的位置top-1,即我们找到了当前点的扩展边界,同时把弹出的矩形合并再压入栈。
建立一个单调(递增)栈,所有元素各进栈和出栈一次。每个元素出栈的时候更新最大的矩形面积。
设栈内的矩形为一个二元组(h, w),h表示矩形的高度,w表示矩形的宽度。
如何入栈并更新呢?
① 如果当前元素比栈顶元素大或者栈为空,则直接压栈(h,1);
② 如果当前元素小于等于栈顶元素,则出栈合并矩形,直到当前元素大于栈顶元素或者栈为空时,合并矩形(h,sum_width)入栈。
③在出栈的过程中更新最大面积和累积宽度
那么重复上述①~③操作,我们可以线性O(n)求出每一组数据的解
注意事项:
单调栈要维护两个信息,一个是高度,另一个是宽度(便于计算矩形面积)
同时令H[n+1]=0,以保证所有矩形全部弹出栈 (最后没有矩形剩余在栈中)

1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #include <string> 5 #include <vector> 6 #include <map> 7 using namespace std; 8 typedef long long LL; 9 const int INF=0x3f3f3f3f; 10 int h[100010];//定义矩形(rectangle)高度h 11 int w[100010];//定义矩形(rectangle)宽度w 12 int stack[100010];//数组模拟矩形的高度单调栈 13 int top; 14 int n; 15 16 void solve()//单调栈算法 17 { 18 long long ans=0; 19 for(int i=0;i<n+1;i++)//1~n+1个矩形扫描一遍 20 { 21 if(h[i]>stack[top])//如果当前矩形高于栈顶直接入栈,宽度为1(符合高度单调递增性质) 22 { 23 stack[++top]=h[i]; 24 w[top]=1; 25 } 26 else//否则就持续弹出栈顶矩形合并 27 { 28 int widthsum=0;//弹出的栈顶矩形合并总宽度 29 while(stack[top]>h[i])//不满足高度单调递增继续弹出 30 { 31 widthsum+=w[top];//累计宽度 32 ans=max(ans,(long long)widthsum*stack[top]);//面积取max 33 top--;//弹出堆顶矩形 34 } 35 stack[++top]=h[i];//把合并好的新矩形入栈(高为H[i],宽为width+1) 36 w[top]=widthsum+1; 37 } 38 } 39 printf("%lld ",ans); 40 } 41 42 int main() 43 { 44 while(scanf("%d",&n)&&n)//多组数据 45 { 46 memset(h,0,sizeof(h));//初始化 47 memset(w,0,sizeof(w)); 48 memset(stack,0,sizeof(stack)); 49 top=0; 50 for(int i=0;i<n;i++)//读入每个矩形高度 51 { 52 scanf("%d",&h[i]); 53 } 54 solve(); 55 } 56 return 0; 57 }
下面是用了STL的代码

1 #include<cstdio> 2 #include<stack> 3 #include <iostream> 4 #include <algorithm> 5 #include<cstring> 6 using namespace std; 7 inline long long read(){ 8 long long x=0,f=1;char c=getchar(); 9 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 10 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 11 return f*x; 12 }//很伤心呢,看错范围了,最初没改成long long 类型的,爆了几次才发现QWQ 13 struct node{ 14 int h,w; 15 };//一个存高度,一个存宽度。 16 long long s[100100],ans=0; 17 int main(){ 18 while(1){ 19 int n=read(); 20 if(n==0)return 0; 21 for(int i=1;i<=n;i++){ 22 s[i]=read(); 23 } 24 stack<node>st; 25 s[n+1]=0; 26 for(int i=1;i<=n+1;i++){ 27 long long W=0; 28 while(!st.empty()&&s[i]<st.top().h){//单调栈!单调递增! 29 W=W+st.top().w; 30 ans=max(ans,W*st.top().h); 31 st.pop(); 32 } 33 st.push((node){s[i],W+1}); 34 } 35 cout<<ans<<endl; 36 ans=0; 37 } 38 }
题目列表:
1047: [HAOI2007]理想的正方形
接雨水
Largest Rectangle in Histogram
最大子序和
Description
输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大。