hdu1506 & leetcode84 Largest Rectangle in a Histogram 矩阵系列(一)

Largest Rectangle in a Histogram

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 7496    Accepted Submission(s): 2106


Problem Description
A histogram is a polygon composed of a sequence of rectangles aligned at a common base line. The rectangles have equal widths but may have different heights. For example, the figure on the left shows the histogram that consists of rectangles with the heights 2, 1, 4, 5, 1, 3, 3, measured in units where 1 is the width of the rectangles:

Usually, histograms are used to represent discrete distributions, e.g., the frequencies of characters in texts. Note that the order of the rectangles, i.e., their heights, is important. Calculate the area of the largest rectangle in a histogram that is aligned at the common base line, too. The figure on the right shows the largest aligned rectangle for the depicted histogram.
 

Input
The input contains several test cases. Each test case describes a histogram and starts with an integer n, denoting the number of rectangles it is composed of. You may assume that 1 <= n <= 100000. Then follow n integers h1, ..., hn, where 0 <= hi <= 1000000000. These numbers denote the heights of the rectangles of the histogram in left-to-right order. The width of each rectangle is 1. A zero follows the input for the last test case.
 

Output
For each test case output on a single line the area of the largest rectangle in the specified histogram. Remember that this rectangle must be aligned at the common base line.
 

Sample Input
  
  
7 2 1 4 5 1 3 3 4 1000 1000 1000 1000 0
 
Sample Output
   
   
8 4000

题意:
给你n,然后告诉你这n段的高度,这样就组成了一个直方图
为你直方图中最大的矩形的面积是多少

解题思路1:dp or 迭代
对于每个高度h[i],迭代找出它的最左边(最左边点到该点之间高度均不小于该点)以及最右边(同理)
对于h[i]而言,如果<=左边的h[i-1],则他的最左边一定是i-1的最左边,然后一直迭代下去 
ps:迭代的过程是很快的
代码(hdu1506):
#include <iostream>
#include <cstdio>
#define maxn 100005
using namespace std;

int n,m;
__int64 h[maxn],le[maxn],ri[maxn];
__int64 ans;

void left()              // 向左迭代
{ 
    int i,j;
    for(i=1;i<=n;i++)
    {
        le[i]=1;
        while(h[i-le[i]]>=h[i])
        {
            le[i]+=le[i-le[i]];
        }
    }
}
void right()             // 向右迭代
{
    int i,j;
    for(i=n;i>=1;i--)
    {
        ri[i]=1;
        while(h[i+ri[i]]>=h[i])
        {
            ri[i]+=ri[i+ri[i]];
        }
    }
}
void solve()                  
{
    int i;
    ans=-1;
    for(i=1;i<=n;i++)     // 更新ans
    {
        if((le[i]+ri[i]-1)*h[i]>ans) ans=(le[i]+ri[i]-1)*h[i];
    }
}
int main()
{
    int i,j;
    while(scanf("%d",&n),n)
    {
        for(i=1;i<=n;i++)
        {
            scanf("%I64d",&h[i]);
        }
        h[0]=h[n+1]=-1;     // 加边界条件
        left();
        right();
        solve();
        printf("%I64d\n",ans);
    }
    return 0;
}

在刷leetcode时也碰到了这个题,见过单调栈的做法,于是也来尝试一下
解题思路2:单调栈 O(n)
因为最终答案是一个高度乘以一个区间,我们只需要找到这个区间即可,可以维护一个栈,栈内元素为位置和高度即 pair<pos,height>,保持高度递增,在出栈时统计以栈顶这个高度为高的矩阵面积更新答案。当遍历一个元素pos时, 如果不小于栈顶高度,直接入栈,如果小于栈顶高度,则将大于该高度的元素全部出栈,每一个元素统计答案时,最 右边为pos-1,最左边元素为栈中它前一个的位置+1,因为前一个元素是最近的比它高度小的元素。遍历完元素后,最 后还要对栈中的元素统计答案。
代码(leetcode 84):
class Solution {
public:
    int largestRectangleArea(vector<int> &heights) {
        stack<pair<int, int>> s;
        pair<int, int> pa;
        int res = 0;
        s.push(make_pair(0, 0)); //增加一个哨兵 栈中位置下标从1开始
        for (int i = 0; i < heights.size(); ++i) {
            if (heights[i] >= s.top().second) {
                s.push(make_pair(i + 1, heights[i]));
            } else {
                while (s.top().second > heights[i]) {
                    int val = s.top().second;
                    s.pop();
                    int st = s.top().first;
                    res = max(res, (i - st) * val);
                }
                s.push(make_pair(i + 1, heights[i]));
            }
        }
        int ed = s.top().first;
        while (!s.empty()) {
            int val = s.top().second;
            s.pop();
            //处理高度相同的情况 一直找到高度小的位置
            while (!s.empty() && s.top().second == val) {
                s.pop();
            }
            if (s.empty()) break;
            int st = s.top().first;
            res = max(res, (ed - st) * val);
        }
        return res;
    }
};



再说一种解法,也是利用单调栈,复杂度O(n)。
这种解法就是把解法一优化一下,思考这个问题:求一个点的左边第一个小于自己的点的位置pos。
可以用单调栈来解决,维护一个单调递增的栈,如果当前元素大于栈顶,则pos为栈顶元素位置;如果小于,则栈一直pop,直到栈顶小于当前元素。实现上可以加一个哨兵,我用python实现的如下。
这种解法高效易懂,比较推荐。


class Solution(object):
    def solvele(self,heights):
        res=[]
        sta=[]
        sta.append((-1,-1))
        for (index,value) in enumerate(heights):
            while value<=sta[-1][1]:
                sta.pop()
            res.append(sta[-1][0])
            sta.append((index, value))
        return res

    def solveri(self,heights):
        res=[0]*len(heights)
        sta=[]
        sta.append((len(heights),-1))
        index=len(heights)-1
        while index>=0:
            value=heights[index]
            while value<=sta[-1][1]:
                sta.pop()
            res[index]=sta[-1][0]
            sta.append((index, value))
            index-=1
        return res

    def largestRectangleArea(self, heights):
        """
        :type heights: List[int]
        :rtype: int
        """
        lelist=self.solvele(heights)
        rilist=self.solveri(heights)
        ans=0
        for i in range(len(heights)):
            ans=max(ans,heights[i]*(rilist[i]-lelist[i]-1))
        return ans









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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值