dp/单调栈

题目:hdu1506

题意:一个柱形图,宽均为1.内部最大的矩形的面积。

解答1:刚开始想的是从某个区间内找小的高遍历所有区间找出最大的矩形。但是复杂度太高啦!

    正确的解答:

从每一条出发,分别求出这一条左边连续比它高的和右边连续比它高的坐标。然后遍历一遍便可以求出最大值。

左右!!!可以分别从左边和右边遍历求!!!

以左边为例:

如果左边那个比它高,那么比它左边还高的一定比它还高!

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int MAXN = 100000 + 10;
long long int a[MAXN],r[MAXN],l[MAXN];
int main()
{
    long long int n;
    while(~scanf("%lld",&n) && n)
    {
        for(int i = 1;i <= n;i++)
            scanf("%lld",&a[i]);
        memset(l,0,sizeof(l));
        memset(r,0,sizeof(r));
        l[1] = 1;
        r[n] = n;
        for(int i = 2;i <= n;i++)
        {
            int tmp = i;
            while(tmp > 1 && a[tmp-1] >= a[i])
                tmp = l[tmp-1];
            l[i] = tmp;
        }
        for(int i = n-1;i >= 1;i--)
        {
            int tmp = i;
            while(tmp < n && a[tmp+1] >= a[i])
                tmp = r[tmp+1];
            r[i] = tmp;
        }
        long long int ans = 0;
        for(int i = 1;i <= n;i++)
        {
            ans = max(ans,(long long int)((r[i]-l[i]+1)*a[i]));
        }
        printf("%lld\n",ans);
    }
    return 0;
}

解答2:从左、从右各维护一个单调栈。

单调栈:栈里面的元素是单调的(从小到大或者从大到小)

1、如果栈里元素从小到大:可以直接找到从该位置往左,第一个比它小的元素。

2、如果栈里元素从大到小:可以直接找到从该位置往右,第一个比它大的元素。

这个题,要找到一个位置往左、往右,第一个比它小的元素(如果比它大就可以扩展)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int MAXN = 100005;
long long int a[MAXN],l[MAXN],r[MAXN];
struct g
{
    long long int h,n;
};
g q1[MAXN],q2[MAXN];
int main()
{
    int N;
    while(~scanf("%d",&N) && N)
    {
        for(int i = 1;i <= N;i++)
            scanf("%d",&a[i]);
        long long int ans = 0;
        int top = 0;
        q1[0].h = 0;
        q1[0].n = 0;
        q1[++top].h = a[1];
        q1[top].n = 1;
        l[1] = 1;
        for(int i = 2;i <= N;i++)
        {
            if(a[i] > q1[top].h)
            {
                q1[++top].h = a[i];
                q1[top].n = i;
                l[i] = i;
            }
            else
            {
                while(q1[top].h >= a[i] && top >= 1)
                    top--;
                q1[++top].h = a[i];
                q1[top].n = i;
                l[i] = q1[top-1].n + 1;
            }
        }
        top = 0;
        q1[0].h = 0;
        q1[0].n = N+1;
        q1[++top].h = a[N];
        q1[top].n = N;
        r[N] = N;
        for(int i = N-1;i >= 1;i--)
        {
            if(a[i] > q1[top].h)
            {
                q1[++top].h = a[i];
                q1[top].n = i;
                r[i] = i;
            }
            else
            {
                while(q1[top].h >= a[i] && top >= 1)
                    top--;
                q1[++top].n = i;
                q1[top].h = a[i];
                r[i] = q1[top-1].n - 1;
            }
        }
        for(int i = 1;i <= N;i++)
            ans = max(ans,(long long int)((r[i]-l[i]+1)*a[i]));
        printf("%lld\n",ans);
    }
    return 0;
}

解答3:依然是单调栈。但是只扫一遍。

有以下结论:

1、如果可以直接入栈,那么它可以向左扩展的长度为1(它自己)。

2、第二个出栈的元素的右宽是前一个出栈的元素的总宽。

3、h的左宽是上一个出栈元素的总宽。

进栈的时候你只能知道它向左扩展的长度(记录下来),出栈的时候你可以算出它向右扩展的长度(出栈意味着它比当前高度要高,就说明它向右扩展不能扩展到当前元素了,那么这个向右扩展的长度是几呢?)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int main()
{
    int n,h;
    int q[100005],l[100005];
    while(~scanf("%d",&n)&&n)
    {
        long long int ans = 0;
        memset(q,-1,sizeof(q));
        memset(l,0,sizeof(l));
        int top = 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,l[top] = 1;//直接进,向左扩展为1(自己)
            else//凡是出栈的元素的高度都比h高(向右扩展不到当前的元素)
            {
                long long int r = 0;
                while(h <= q[top])
                {
                    ans = max(ans,q[top]*(l[top]+r));
                    r += l[top--];
                }
                q[++top] = h;
                l[top] = r + 1;//加上自己
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值