今天小初来说说单调栈,关于此算法,小初还是有很多不足之处,还请求有人能够指出来
介绍
单调栈顾名思义,就是栈中元素呈现单调性。而所谓单调就和数学里的定义一样,是一列数从小到大排列或从大到小来排列,所以我们阔以想象单调栈就是栈里面的数是完全有序的。
应用
小初在刚刚得知这个算法的时候是一脸懵的,觉得没有什么用呀,但是仔细想想,这个算法的实用性还是比较高的。
我们先把单调栈的性质列举出来
1.单调栈里的元素具有单调性
2.元素加入栈前,会在栈顶端把破坏栈单调性的元素都删除
3.使用单调栈可以找到元素向左遍历第一个比他小的元素,也可以找到元素向左遍历第一个比他大的元素。
(也就是说在元素进栈前他向左拓展的区间已经确定,在出栈前她能向右拓展的区间也能确定)
那么我们阔以根据这些性质把应用列举出来,如下:
1.最基础的应用就是给定一组数,针对每个数,寻找它和它右边第一个比它大的数之间有多少个数。
2.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列的长度最大。
3.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大。
例题
我们来康康小初写的第一个关于单调栈应用的题目
小初的直达车
题解
由题意可得,这道题是关于小初列举的第二个应用,而要求出最大的矩形面积,也就是基本的长乘宽,因为纸片的宽度恒为1,所有我们只需要考虑用户输入的纸片长度,参考本题的时间限制和数据大小,所有我们必须要控制迭代次数,本题的思路其实很简单,就是找出在用户输入的长度,往左右边找比其长度小的纸片,因为如果左右两边的纸片大于或等于该纸片,那么矩形就阔以沿着左右两边进行扩散,这样我们就知道了矩形的宽度,而长度就是用户输入的值,只要将所有的长乘以宽并找出最大的就行了,是不是很简单呢,小初青涩的代码如下:
#include<stdio.h>
#include<string.h>
long long int r[100100];//记录左边界
long long int l[100100];//记录右边界
long long int h[100100];//记录纸片高度
long long int xh[100100];//记录位置
int main(void)
{
int n,i,j,k,t;
long long int a,b,end;
while(1)
{
scanf("%d",&n);
if(n==0)
break;
for(i=1; i<=n; i++)
scanf("%lld",&h[i]);
xh[0]=t=0;//寻找左边界
h[0]=-1;//将不存在的最左边设置为-1,表示栈为空时一定能够入栈,下同
for(i=1; i<=n; i++)//对该纸片的左边进行入栈出栈,规律是将破坏单调递增的数出栈,合法的入栈,下同
{
while(h[xh[t]]>=h[i])
t--;
l[i]=xh[t];
xh[++t]=i;
}
t=0;
xh[0]=n+1;//寻找右边界
h[n+1]=-1;
for(i=n;i>0;i--)//同上
{
while(h[xh[t]]>=h[i])
t--;
r[i]=xh[t];
xh[++t]=i;
}
for(i=1;i<=n;i++)//寻找最大值
{
if(i==1)
end=(r[i]-l[i]-1)*h[i];
else
{
a=(r[i]-l[i]-1)*h[i];
if(a>end)
end=a;
}
}
printf("%lld\n",end);
}
}
那么,单调栈就暂时告一段落啦。谢谢浏览。