poj-2559 单调栈

题目链接:https://vjudge.net/problem/POJ-2559

以前接触这道题的时候还以为这是单调栈的模板题。但现在觉得单调栈数据结构远比这道题解法好理解得多。

解法:

初次见这道题的话肯定是先分析问题(鬼tm才一开始就确定单调栈是解法)。

首先:按照常规思路,试一试从左到右扫描,每扫到一个位置i,高度为h[i],计算位置i的贡献attri[i]:当第i个棍一定会被贡献(可以只是贡献一部分高度),而且第i个棍必须是最后面的一个。由于第i个棍贡献不同高度时答案可能会不同,你要找到最大的答案视为attri[i]。由于attr[i]对应的高度h_attr[i]肯定是第i个之前并且小于等于h[i]。也就是说你计算attr[i]时只用考虑前i个中高度小于等于h[i]的就行了。那么第i个考虑进去的时候前面只要比他高的被它踢出数组就可以,每个位置都这样处理的话,数组里面一定是一个非递减序列。这样不就成了单调栈吗。

poj-2559 单调栈_第1张图片

还没完,其次就是要计算那个attr[i]了,计算attr[i]你有两个选择:choose1.第i个进栈时就计算       choose2.第i个被踢出时计算

此处注意了,我把头想破发现两个方法都得每次把栈扫一遍,时间复杂度不允许

现在把attr[i]定义改一下:第i个的h[i].h[i]所有高度都被贡献上时的最大值。这样一来和之前一样还是要用单调栈。不过计算attr[i]就有了途径:choose2

现在可以考虑怎么计算,既然i被选中了,那么你得考虑i向左连续和向右连续各有多少高度至少为h[i]的。假设i被k踢出了栈,那么必知h[i]>h[k],那么其实i向右连续就有k-i-1个高度至少为h[i]的。至于向左连续设有lenth[i]个,一开始为1(它自身),你想想,假设i之前把j踢出了栈,那么lenth[i]+=lenth[j](此处有点dp思想),总结那不就是i每踢一个前面比它高的j,那么lenth[i]都得加上length[j]。

向左向右的连续各有多少高度至少为h[i]的有了思路,那么attr[i]不就出来了吗:attr[i]=(lenth[i]+k-i-1)*h[i]     ------i是被k踢出的栈

答案就是:max(attr[i])  {i=0}

有一个细节:若要确保attr[i]的i从0到n-1都能考虑得到,你得在最后加上一个最小的高度把前面所有的高度都踢出去。即:h[n]=0;

接下来就是代码实现部分了:

#include 
#include 
#include 
#include 

using namespace std;
typedef long long ll;
const int max_n=1e5+10;
ll A[max_n];//第i个对应的高度为A[i]
ll sta[max_n];//存储高度的栈
ll len[max_n];//存储上面所讲的length[i]的栈
ll pos[max_n];//存储下标的栈
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        if(!n) break;
        for(int i=0;iA[i])
                {
                    ret=max(ret,sta[top-1]*(len[top-1]+i-pos[top-1]-1));
                    x+=len[top-1];
                    top--;
                }
            }
            sta[top]=A[i];
            pos[top]=i;
            len[top++]=x;
        }
        printf("%lld\n",ret);
    }
}

 

你可能感兴趣的:(单调栈,poj-2559,单调栈)