【单调栈】hdu1506 Largest Rectangle in a Histogram ----简单了解单调栈

这个题是可以用动态规划,或者是单调栈、其实实质是差不多的;

这里讲一下单调栈,参考博客http://blog.csdn.net/dgq8211/article/details/7740610

 

1. 什么是单调栈

单调栈就是保持了单调性和栈的性质;

单调递增的栈就是从栈尾到栈顶是单调递增的;

2. 单调栈能够解决的问题

1) 以自己为最小或最大值找到最大的区间,(对应 单调递增/单调递减);

2) 给定一个区间,找到这个区间的最大或最小值;

3. 单调栈的性质

1) 对于第一个出栈元素,它的右宽一定为0;

2) 对于第二个出栈元素,它的右宽为前一个出栈元素的总宽;

3) 对于第三个出栈元素,它的右宽为第二个出栈元素的总宽;

       …………

4) 好了,终于遇到了比自己小的元素可以入栈了,那么入栈元素的左宽为上次出栈元素的总宽+1(自身);(若无出栈元素,则左宽为1(自身)

5) 最后将栈中所有元素出栈考虑所有情况;

简言之,出栈元素的右宽为上个出栈元素的总宽,入栈元素的左宽为上个出栈元素的总宽+1;

单调递增  左宽就是比向左数的x个数小,右宽就是比向右数的y个数都小;

单调递减  左宽就是比向左数的x个数大,右宽就是比向右数的y个数都大;


4. 以下面数据模拟一下单调递减栈的操作

 

1入栈,无出栈元素,左宽为1

5入栈,无出栈元素,左宽为1

5出栈,第一个出栈右宽为0

4入栈,左宽为上个出栈元素的总宽+1=2

8入栈,无出栈元素,左宽为1

8出栈,第一个出栈,右宽为0

6入栈,左宽为上个出栈的总宽+1

7入栈,无出栈元素,左宽为1

7出栈,第一个出栈,右宽为0

6出栈,第二个出栈,右宽为上个元素的总宽:1

4出栈,第三个出栈,右宽为上个元素的总宽:3

3入栈,左宽为上个出栈的总宽+16

3出栈,第一个出栈右宽为0

2入栈,左宽为上一个出栈的总宽+17

//全部出栈:

2出栈,第一个出栈,右宽为0

1出栈,第二个出栈的,右宽为上个出栈的总宽:7

 

5. 单调栈的实现

hdu1506(单调递减栈)poj2339,hrbustoj 2326(需long long)

单调递减栈如果是以各个元素为最大值找到最大区间的话  q[0]=inf, h=inf-1;

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstdlib>
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
#define N 100005
 
int q[N]={-1},w[N];//w记录左宽;
int main()
{
    int n,h;
    while(scanf("%d",&n)&&n)
    {
        int top=0;
        ll ans=0;
        for(int i=1;i<=n+1;i++)
        {
            if(i!=n+1)
                scanf("%d",&h);
            else
                h=0;
            if(h>q[top])
                q[++top]=h,w[top]=1;
            else
            {
                ll cnt=0;  //第一个出栈的右宽为0;
                while(h<=q[top])
                {
                    ans=max(ans,(w[top]+cnt)*q[top]);  //(左宽+右宽)*高度;
                    cnt=cnt+w[top--];  //第(i>1)出栈的右宽为上一个的总宽;
                }
//                终于找到比自己小的数字了,可以入栈了,入栈会得到左宽,左宽为上一个出栈元素的总宽+1;
                q[++top]=h;
                w[top]=cnt+1;
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


若是还想记录每个元素作为最小值的区间呢?

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<iostream>
#include<cstdlib>
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
#define N 100005


int q[N]={-1},w[N];//w记录左宽;
int a[N],L[N],R[N];//记录指定元素左宽右宽,输出为左右区间;
int main()
{
    int n,h;
    stack<int > st;
    while(scanf("%d",&n)&&n)
    {
        while(!st.empty()) st.pop();
        L[1]=1,R[n]=0;  //记录的是左宽右宽;
        int top=0;
        ll ans=0;


        for(int i=1;i<=n+1;i++)
        {
            if(i!=n+1)
                scanf("%d",&h);
            else
                h=0;


            if(h>q[top])
            {
                //无出栈元素,左宽为1;
                q[++top]=h,w[top]=1;
                L[i]=1;/****/
            }
            else
            {
                ll cnt=0;  //第一个出栈的右宽为0;
                while(h<=q[top])
                {
                    int id=st.top();st.pop();
                    R[id]=cnt;/***/
                    ans=max(ans,(w[top]+cnt)*q[top]);  //(左宽+右宽)*高度;
                    cnt=cnt+w[top--];  //第(i>1)出栈的右宽为上一个的总宽;
                }
//                终于找到比自己小的数字了,可以入栈了,入栈会得到左宽,左宽为上一个出栈元素的总宽+1;
                q[++top]=h;
                w[top]=cnt+1;
                L[i]=w[top];/***/
            }
            st.push(i);
        }
        printf("%I64d\n",ans);  //输出最大面积;
        
        for(int i=1;i<=n;i++)
        {
            printf("%d:%d %d\n",i,i-L[i]+1,i+R[i]);  //输出以a[i]为最小值的区间;
        }
    }
    return 0;
}

答案:保存各位的左宽右宽:用stack维护下标即可~

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值