单调栈总结

单调栈一般可以对于给定的数列,求每个值v在他左右的区间[l,r]是最小值(最大值),通过单调递增(递减)栈来实现。

1.用处

求每个值v是哪个区间[l,r]的最小(大)值,一般还会和区间[l,r]有关系

poj2796 Feel good 

给定数列 要求一个区间内最小值乘区间和,的最大值。

poj2559 Largest rectangle in a Histogram 

给定几个不同高度的矩形,求能画出的最大矩形面积 

其实就是区间最小值*区间长度,的最大值

poj3494 Largest Submatrix of All 1’s

给定01矩阵,求全是1的矩阵的最大面积

经过对每一行处理,他可以向上长多少,就转换成poj2559的直方图样子,求区间最小值*区间长度的最大值即可。

poj3250 Bad Hair Day

给定一列牛,求每头牛能看到他右侧几头牛,求和。

求比他矮的,所以是递减栈,而且只需要一侧,所以只记录r也可以。每个值作为区间最大值,的区间长度,求和。


2.步骤(以递增栈为例)

a.初始化值和区间[l,r]

可用结构体

struct node{

    int v,l,r;//v[l,r]内的最小值

};

vi题目给定,l,r如果表示vi是[l,r]的最小值,一般初始化为[i,i]

b.for i = 1 to n 遍历准备入栈

if 栈为空 || 准备入栈元素v[i]  >= 栈顶

入栈

else

while 栈不为空 && 栈顶仍然比准备入栈元素大

node t = stk.top();stk.pop();

可以根据t,stk.top(),v[i]之间的大小关系,更新stk.top(),v[i]的区间[l,r] // 更新stk.top()前要记得判断栈是否为空

用t更新答案

退出while 可以将v[i]入栈了

c.在所有元素遍历完后,栈内仍有元素,可能为答案,所以要清空栈
while 栈不为空
node t =stk.top();stk.pop();
用t更新stk.top()  // 更新stk.top()前要记得判断栈是否为空

用t更新答案


模版

#include <iostream>

#include <cstdio>

#include <stack>

using namespacestd;

const int maxn =8e4 +5;

typedef longlong ll;

struct node{

    int h,l,r;//h[l,r]内最小值

}cow[maxn];

stack<node> stk;


int main()

{

    int n;

    scanf("%d",&n);

    for (int i =0; i < n; i ++) {

        scanf("%d",&cow[i].h);

        cow[i].l =cow[i].r = i;

    }

    ll ans =0;

    for (int i =0; i < n; i ++) {

        if(stk.empty() ||cow[i].h >=stk.top().h )stk.push(cow[i]);

        else {

            while(!stk.empty() &&cow[i].h <stk.top().h){

                node t =stk.top();stk.pop();

                ans += t.r;//根据题目计算答案

                cow[i].l = t.l;

                if(!stk.empty())stk.top().r = t.r;

            }

            stk.push(cow[i]);

        }

    }

    while (!stk.empty()) {

        node t =stk.top();stk.pop();

        ans += t.r;//

        if(!stk.empty())stk.top().r = t.r;

    }

    printf("%lld\n",ans);

    return0;

}


ps:

1.注意根据题目是递增栈还是递减栈,是否严格增减

2.超时的话手写栈

3.根据题目,决定答案怎么更新,不拘泥于形式,比如SCU2511

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值