线性结构——单调栈
①定义:栈内的元素,按照某种方式排序(单调递增或单调递减)如果新入栈的元素破坏了单调性,就弹出栈内元素,直到满足单调性
②优点:可以很方便地求出某个数左边或者右边第一个比他大或者小的元素,总时间复杂度为O(n)
③如何维护单调栈(以递增为例)
进栈操作:每次入栈前先检验栈顶元素和进栈元素x的大小,如果小于x,就让x
直接入栈。如果栈顶元素大于等于x,那么出栈,直到栈空或者栈顶元素小于x。
【例1】最大矩形面积(poj2559)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=100002; 4 int h[N],n; 5 int line[N],L[N],R[N],top=0; 6 int main(){ 7 scanf("%d",&n); 8 for(int i=1;i<=n;i++) 9 scanf("%d",&h[i]); 10 for(int i=1;i<=n;i++){ 11 while(top&&h[line[top]]>=h[i]) top--; 12 L[i]=top?line[top]+1:1; 13 line[++top]=i; 14 } 15 top=0; 16 for(int i=n;i>=1;i--){ 17 while(top&&h[line[top]]>=h[i]) top--; 18 R[i]=top?line[top]-1:n; 19 line[++top]=i; 20 } 21 int maxn=0; 22 for(int i=1;i<=n;i++){ 23 int ans=h[i]*(R[i]-L[i]+1); 24 maxn=max(maxn,ans); 25 } 26 cout<<maxn<<endl; 27 return 0; 28 }
【例2】玉蟾宫(luogu P4147)
题目传送门
讲一讲我自己的思路(其实是同学教的)吧,似乎不是很好的解?但我感觉比较好理解……
首先把输入的字符矩阵转移为数字,即s[i][j]代表第i行第j列这一格上面连续的F的个数(如果这一格是S的话就是0),所以预处理之后就把样例变成了这样:
0 1 1 1 1 1
1 2 2 2 2 2
0 0 0 3 3 3
1 1 1 4 4 4
2 2 2 5 5 5
代码实现:
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){char ch;cin>>ch;if(ch=='F') s[i][j]=s[i-1][j]+1;}
然后就有点类似于上面那道题了,每一行分开分析,找到每一个位置可以向左右扩展的最长的长度,这是矩形的长,而组成的矩形的宽就是这一格中对应的数字,所以向左右两边扩展的条件就是要扩展的那一格中的数字一定大于等于当前扫描到的这一格的数字。(似乎没太讲清楚啊QAQ)每一行处理出一个可以组成的矩形的最大面积,用一个maxn取最大值就可以找到最后的答案了。
代码实现:
for(int i=1;i<=n;i++){int ans=0;//用于记录当前这一行的矩形最大面积for(int j=1;j<=m;j++){int l=j,r=j;//l代表左边能扩展到的最远位置,r代表右边能扩展到的最远位置while(l>=1&&s[i][l]>=s[i][j]) l--;l++;while(r<=m&&s[i][r]>=s[i][j]) r++;r--;ans=max(ans,s[i][j]*(r-l+1));//当前这一行的最大矩形面积 }maxn=max(ans,maxn);//每一行的最大矩形面积比较取最大值 }
以下完整代码(没开O2的时候T了两个点QAQ)
1 // luogu-judger-enable-o2 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 const int N=1002; 7 int K,n,m; 8 int s[N][N]; 9 int maxn=0; 10 int max(int x,int y){ 11 return x>y?x:y; 12 } 13 int main(){ 14 //scanf("%d",&K); 15 //while(K--){ 16 memset(s,0,sizeof(s)); 17 scanf("%d%d",&n,&m); 18 for(int i=1;i<=n;i++) 19 for(int j=1;j<=m;j++){ 20 char ch; 21 cin>>ch; 22 if(ch=='F') s[i][j]=s[i-1][j]+1; 23 } 24 for(int i=1;i<=n;i++){ 25 int ans=0;//用于记录当前这一行的矩形最大面积 26 for(int j=1;j<=m;j++){ 27 int l=j,r=j;//l代表左边能扩展到的最远位置,r代表右边能扩展到的最远位置 28 while(l>=1&&s[i][l]>=s[i][j]) l--; 29 l++; 30 while(r<=m&&s[i][r]>=s[i][j]) r++; 31 r--; 32 ans=max(ans,s[i][j]*(r-l+1));//当前这一行的最大矩形面积 33 } 34 maxn=max(ans,maxn);//每一行的最大矩形面积比较取最大值 35 } 36 printf("%d\n",maxn*3);//最后别忘了乘3 37 //} 38 return 0; 39 }