hdu 1506 Largest Rectangle in a Histogram(单调栈||dp)

来源:互联网 发布:ubuntu查看硬盘总大小 编辑:程序博客网 时间:2024/05/29 14:05
小记:这里学到了一个新的数据结构,单调栈。从最初的自己动手实践单调栈,到理解清楚单调栈并用于解决题目。


思路:所谓单调栈,就是栈里的元素从栈底到栈顶都是递增或者递减的的栈 称作单调栈。  下面还有dp解法
对于此题:我们从左往右依次看矩高,每个栈元素存两个值,一个是矩高一个是当前是第几个矩。栈按矩高递增存放。
一旦对于某一个矩高,栈顶的矩高大于它,那么我们就要清算单调栈里所形成的矩形的面积了。保存最大的那个。然后继续往后面的矩高计算。
我自己最开始的思路是,从头开始,对于当前的矩高,从其左边第一个矩高开始找,找到的第一个小于它的矩高的值时,那么对于当前矩高,其所能形成的最大矩形面积就确定了。保存起来,然后计算下一个矩高。 
然后,我们结合单调栈来想,单调栈不就是直接帮我找到了它从它的左起的第一个小于它的矩高么。而对于左边是大于它的矩高,我们是不会入栈的,而是直接开始进行清算,那么我就是相当于利用单调栈实现了O(n)时间复杂度对所有矩高的处理,想法的各个部分得到解决。代码就简单了。
举例:
7 2 1 4 5 1 3 3
    单调栈为了便于清算,先入栈(0,0)(起点,矩高)
  然后2 > 0,入栈(1,2);
然后1 < 2,开始清算,栈顶元素出栈,计算矩形面积,得到2,maxm = 2;
然后入栈元素(1,1);
再是入栈,(3,4) (4,5)
然后1 < 5,开始清算,栈顶元素判断直到小于当前的矩高。计算得出最大的矩形面积,maxm = 8
再是入栈,(6,3), (7,3)
最后为了便于清算,最后要加个0矩高,
所以0 < 3,栈顶元素判断直到小于0矩高,计算保存最大矩形面积,maxm = 8
栈是否为空,是结束,否则,以最后点为终点,计算保存最大矩形面积,maxm = 8
输出答案

#include <stdio.h>#include <string.h>#include <stack>#include <iostream>#define max(a,b) (a)>(b)?(a):(b)using namespace std;const int MAX_ = 100010;long long h[MAX_];struct node{    long long s,h;};stack<node>s;int main() {    long long maxm,k;    int n;    while(scanf("%d",&n),n){        node cur;        while(!s.empty())s.pop();        cur.s = cur.h = 0;        s.push(cur);        maxm = -1;        for(int i = 0; i < n; ++i){            scanf("%I64d",&k);            cur.s = i;            while(k < s.top().h){                cur = s.top();                s.pop();                long long t = (long long)(i - cur.s) * cur.h;                maxm = max(maxm,t);            }            cur.h = k;            s.push(cur);        }        while(!s.empty()){            long long tmp = (long long)(n - s.top().s)*s.top().h;            maxm = max(maxm,tmp);            s.pop();        }        printf("%I64d\n",maxm);    }    return 0;}


dp解法;

我们只要知道对于每一个矩形,在其左边和在其右边大于或等于它的矩形的个数,然后就是这个矩形所能形成的最大矩形面积了,高为自己的高。

然而如果我们对每一个矩形都用暴力的方法去求解,那么必然TLE。

如何优化它?

那些字符串匹配算法可以给些提示。

当我们计算左边连续的大于等于当前矩形高时,假设当前矩形是第i个,那么我们已经知道了第i-1个矩形左边所能达到最长连续矩形个数。 

如果第i个矩形的高小于第i-1个矩形的高,那么我们就可以直接利用i-1的最长连续矩形个数了,直接看它最左边的那么矩形,那个矩形的高比当前的高大的话

那么就又可以加上来算到当前矩形所能达到的最长连续矩形长度里,直到大于前面某个矩形的高时,结束.

右边同理。

l[i]  -> 第i个矩形左边大于等于它的高的最长连续矩形个数

r[i] ->  第i个矩形右边大于等于它的高的最长连续矩形个数


初始化 l[i] = r[i] = i;

l[i] = l[l[i]-1]  ( l[i] > 0 && a[l[i]-1] >= a[i], i = 0, 1, 2,..., n-1)

r[i] = r[r[i]+1]  ( r[i] +1 < n && a[r[i]-1] >= a[i], i = n-1, n-2, n-3,..., 0)


最后的answer就是 max((r[i] - l[i] + 1) * a[i])

#include <iostream>#include <stdio.h>#include <string.h>#include <math.h>#include <stdlib.h>#include <map>#include <set>#include <vector>#include <stack>#include <queue>#include <algorithm>using namespace std;#define mst(a,b) memset(a,b,sizeof(a))#define REP(a,b,c) for(int a = b; a < c; ++a)#define eps 10e-8const int MAX_ = 100010;const int N = 3000010;const int INF = 0x7fffffff;int a[MAX_];int l[MAX_], r[MAX_];int main(){int k, n;long long maxi;while(scanf("%d", &n), n){    REP(i, 0, n){            scanf("%d", &a[i]);            l[i] = i;            r[i] = i;    }    REP(i, 0, n){            while(l[i]  > 0 && a[l[i] - 1] >= a[i]){                l[i] = l[l[i] - 1];            }    }    for(int i = n - 1; i > -1; --i){            while(r[i] + 1 < n && a[r[i] + 1] >= a[i]){                r[i] = r[r[i] + 1];            }    }    maxi = -1;    REP(i, 0, n){            long long tmp = (long long)(r[i] - l[i] + 1) * a[i];            maxi = max(maxi, tmp);    }    printf("%I64d\n", maxi);}return 0;}




0 0