介绍
顾名思义,即入栈是遵循单调原则,其目的是把区间分成若干区间,使每个区间的最右端的元素是该区间的
最小值,可以求出一个元素向左(或向右)所能扩展到的最大长度,并不是说在这一段区间内是单调的,而
是保证在该区间内该元素一定是最大或最小。
操作
每次读入一个元素A,判断该元素的数值与栈顶元素数值的大小关系,如果比栈顶元素大则入栈,否则弹栈
直至栈顶元素小于该元素后则入栈,这样的入栈方式保证了进栈后该元素与栈中前一个元素的这段区间内
(左开右闭)最小值为元素A,如果区间内存在元素b小于A元素,而此时元素b不在栈内,证明元素b已被
弹出,弹出的条件是栈中进入了比它更小的元素,从而这个更小的元素进栈,与这段区间内只有元素A进栈
相矛盾,故元素A为该区间的最小值。所以栈中的每个元素掌控的便是一个区间
证明
上面说明了栈中每个元素是一段区间的最小值,这样当进入新元素C时,当元素C比栈顶元素不小时,显然无
法拓展,当比栈顶元素大时,每弹出一个元素,实际上这个元素代表的区间就可以被拓展,而第一个无法弹
出的元素肯定是第一个C无法向左拓展的元素,这样向左拓展就做完了,哪向右拓展呢,每次进入新元素C,
而弹出B时,C肯定是B向右拓展的极限,而每个元素只进栈了一次、出栈了一次,这样用O(n)解决了此问
题。注意最后所有的元素的操作完了以后别忘了弹出栈中剩余的元素。
经典问题描述
给出连续矩阵(10^5)的高,求最大矩阵面积. [POJ-2559]
分析
最大面积和选取的连续矩阵中最低高度有关,所以枚举最低高度,然后用单调栈去拓展即可。复杂度
O(n)
code ##cpp
#include<cstdio>
#include<stack>
#include<iostream>
using namespace std;
const int maxx=100005;
int a[maxx];
struct node
{
int l,r;
long long num;
};
stack<node>s;
void pout(node tmp)
{
printf("min:%lld(%d,%d)\n",tmp.num,tmp.l,tmp.r);
}
int main()
{
int n;
scanf("%d",&n);
while(n>0)
{
long long ans=0;
while(!s.empty())
s.pop();
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
node tmp;
tmp.num=a[i];
tmp.r=i;
tmp.l=i;
int delta=i;
while((!s.empty())&&(s.top().num>=a[i]))
{
s.top().r=i-1;
long long sum=0;
sum=s.top().num*(s.top().r-s.top().l+1);
if(sum>ans)
ans=sum;
delta=s.top().l;
// pout(s.top());
s.pop();
}
// pout(tmp);
tmp.l=delta;
// printf("%dtest:\n",delta);
// pout(tmp);
s.push(tmp);
}
while(!s.empty())
{
s.top().r=n;
long long sum=0;
sum=s.top().num*(s.top().r-s.top().l+1);
if(sum>ans)
ans=sum;
// pout(s.top());
s.pop();
}
cout<<ans<<endl;
scanf("%d",&n);
}
return 0;
}