前言
单调栈即栈的一种。
单调栈的定义
既然是来学单调栈的,我相信进来看的都是理解栈的定义的人,所以关于栈是什么我就不多说了。单
调栈,顾名思义,就是存进栈的数是以单调递增或者单调递减的形式存在的,并不是严格的单调递增
或递减,是可以等于的。
下面举个例子:{1 ,2 ,3 ,4 ,4 ,5 ,5 ,6} 假使这些数按这个顺序存放在栈里面,他是完全按
从小到大的顺序,也就是升序,就是单调栈。反之,降序也是单调栈。
单调栈的性质
1.单调栈里的元素具有单调性(即递增或递减)
2.元素加入栈前,会在栈顶端把破坏栈单调性的元素都删除
3.使用单调栈可以找到元素向左遍历第一个比他小的元素,也可以找到元素向左遍历第一个比他大的
元素。
4.单调栈的维护是 O(n) 级的时间复杂度,因为所有元素只会进入栈一次,并且出栈后再也不会进栈
了。
单调栈的应用
单调栈一般是用来找大序列里面的每个子序列。
例题:
题目: 直方图周最大的矩形
输入格式
输入包含几个测试用例。
每个测试用例占据一行,用以描述一个直方图,并以整数n开始,表示组成直方图的矩形数目。
然后跟随n个整数h1,…,hnh1,…,hn。
这些数字以从左到右的顺序表示直方图的各个矩形的高度。
每个矩形的宽度为1。
同行数字用空格隔开。
当输入用例为n=0时,结束输入,且该用例不用考虑。
输出格式
对于每一个测试用例,输出一个整数,代表指定直方图中最大矩形的区域面积。
每个数据占一行。
请注意,此矩形必须在公共基线处对齐。
数据范围
1≤n≤1000001≤n≤100000,
0≤hi≤1000000000
输入样例:
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
输出样例:
8
4000
题解:
首先讲一下样例一:2 1 4 5 1 3 3
这里首先找2,2的右边为1比它矮,左边没有他的长度也就最大可能就为1,所以面积为2*1=2
然后找1,它往左边延长和右边延长,可以看到它延长过后就是7,所以它的面积是7*1=7
再找4,它不能往左延长,因为左边第一个就比它矮,它可以往右延长一位,所以面积是2*4=8
再找5,它左右都延长不了,所以他的组成最大矩形是5*1=8
然后这个1和前面的1一样也是7
这个3和后面的3都可以以上述思想证明组成的面积最大为6
所以前面所述的矩形最大面积就为4组成的矩形,就为8。
找到每一个矩形就是找每一个数的左右俩边和他一样高或者比他高的长度有多长,再乘以他的高就是
以他为宽的高度,在此,我们就可以利用单调性的性质来做此题,即如果满足进栈数大于栈顶就一直
进栈,如果不满足进栈数大于栈顶就直接出栈,因为此时进栈数即右边的数比左边这个子序列要矮所
以右边这个数此时不计算即现在进栈的数此时不计算一直计算比进栈数高的栈里面的数直到可以进栈
或者到栈底,即本栈变为空栈时,算的时候还需要将一样高或者比他高的数累加起来。
上代码:
#include<stdio.h>
#include<math.h>
long long int max(long long int a,long long int b)//比较大小
{
return a>b?a:b;
}
long long int num[100010],zhan[100010],a[100010];
int main()
{
long long int i,k,n,ans;
while(scanf("%lld",&n))
{
if(n==0)break;
for(i=0;i<n;i++)scanf("%lld",a+i);//输入
i=k=ans=a[n]=0;//将最后一个的后面那个赋值为0,所以肯定到最后的后面那个时要出栈就能算完
while(i<=n)
{
if(k==0||a[i]>=zhan[k-1])
{//如果进栈的数字比栈顶大就入栈并默认左右边一样高的个数为1
zhan[k]=a[i];
num[k]=1;
k++;
}
else
{//出栈的同时计算矩形的长度
long long int lon=0;
while(k>0&&a[i]<zhan[k-1])
{//一直重复出栈的操作,出去一个即比进栈数大的有一个,拿变量lon存入
lon+=num[k-1];
ans=max(ans,zhan[k-1]*lon);//记住最大的矩形面积
k--;
}
zhan[k]=a[i];
num[k]=lon+1;
k++;
}
i++;
}
printf("%lld\n",ans);
}
}