单调栈

来源:互联网 发布:淘宝有哪些直通车 编辑:程序博客网 时间:2024/04/26 08:12

从某一个元素开始,以该元素为最值,一直向前延伸找最优值(若需要两个方向的解,则再从右往左扫描一遍即可).每个元素最多进出栈一次,复杂度O(n)

应用:

①栈维护上升序列求解不小于i的最大区间: 

题目链接: hdu1506 Largest Rectangle in a Histogram 

求直方图能覆盖的最大矩阵的面积,则矩阵的高度为其中直方图中的最小高度。对某个直方图h[i],向两边搜寻不小于该高度的区间。

以左边界为例,使用单调栈维护一个递增序列的下标,每次取栈顶j进行比较。若h[j] >= h[i],则说明i可以延伸至j的左边界,则更新i的左边界。由于高度小于h[i]的直方图可以覆盖i的区间,大于h[i]的直方图直接被i中断,所以j对后续节点并无作用,将j弹出栈。

直到h[j] < h[i],说明i不能再向更远处延伸,将i进栈。

代码如下:

#include <cstdio>#include <stack>using namespace std;#define N 100005int a[N], l[N], r[N];stack<int> h;int main(){    int n;     while(scanf("%d", &n) && n){        h.push(0);        for(int i = 0; i < n; i ++){             scanf("%d", &a[i]);            l[i] = 1;            r[i] = 1;        }        for(int i = 1; i < n; i ++){            while(!h.empty() && a[i] <= a[h.top()]){                l[i] += l[h.top()];                h.pop();            }            h.push(i);        }        while(!h.empty())            h.pop();        h.push(n - 1);        for(int i = n - 2; i >= 0; i --){            while(!h.empty() && a[i] <= a[h.top()]){                r[i] += r[h.top()];                h.pop();            }            h.push(i);        }        while(!h.empty())                h.pop();        long long max_val = 0;        for(int i = 0; i < n; i ++){            long long temp = (long long)a[i] * (l[i] + r[i] - 1);            if(temp > max_val)                max_val = temp;        }        printf("%I64d\n", max_val);    }}

②栈维护下降序列求解不大于i的最大值

题目链接:hdu3410 Passing the Message 

left messenger为左边不超过h[i]的最高者j.因为若h[j] > h[i],则j一定先从别处得到message。维护一个下降序列的下标,保存当前的最高值、次高值……

每次将h[i]与栈顶比较,若大于栈顶,说明left messenger的位置不晚于这个位置,而且栈顶元素会被i挡住,所以对于i以后的点,栈顶元素也没有意义了,应该出栈。直到小于栈顶的高度,说明i的视线会被栈顶的元素挡住,则上一次的栈顶元素为left messenger。同理,求解right从右往左扫描即可。

代码如下:

#include <cstdio>using namespace std;#define N 50005int h[N], stack[N], L[N], R[N];int main(){int n, tc, ca = 0;scanf("%d", &tc);while(tc --){scanf("%d", &n);for(int i = 1; i <= n; ++i)scanf("%d", &h[i]);stack[0] = 1;   //维护一个下降序列,栈中存放下标int top = 0;for(int i = 2; i <= n; ++i){int cur = 0;while(top >= 0 && h[stack[top]] < h[i])cur = stack[top], --top;L[i] = cur;   //最后一个比h[i]小的位置stack[++top] = i;}stack[0] = n, top = 0;for(int i = n - 1; i > 0; --i){int cur = 0;while(top >= 0 && h[stack[top]] < h[i])cur = stack[top], --top;R[i] = cur;stack[++top] = i;}printf("Case %d:\n", ++ca);for(int i = 1; i <= n; ++i){printf("%d %d\n", L[i], R[i]);}}return 0;}

单调栈不涉及区间长度,可以一直往一个方向延伸,所以只在栈顶进出栈。

当需要在一定范围内搜索时,要用单调队列,队头为该范围内的最优值,当下标不在有效范围时,队头要出队。由于篇幅有限,单调队列令开一篇记录。

0 0
原创粉丝点击