POJ.2559[leetcode.84]直方图最大矩形及二维情况

LeetCode Hard 84 . Largest Rectangle in Histogram
POJ 2559 Largest Rectangle in a Histogram | N=10W
GDUFE OJ 1181 | N=5000
给出一个直方图,求里面的最大矩形面积,如下图面积为10。

暴力 O(n^2)

最简单粗暴的办法是对于每个方块,我们求出它向左和向右能延伸的最远距离,再乘以它的自身高度就行了。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define clr( a , x ) memset ( a , x , sizeof (a) );
#define RE freopen("1.in","r",stdin);
const int maxn = 1005;

int largestRectangleArea(int* heights, int heightsSize) {
    int ans = 0;
    for (int i = 0; i < heightsSize; i++) {
        int dis = 0;    //i方块能延伸的最远距离
        for (int j = i + 1; j < heightsSize; j++) {
            if ( heights[i] <= heights[j]) dis++;
            else break;
        }
        for (int j = i; j >= 0; j--) {
            if ( heights[i] <= heights[j]) dis++;
            else break;
        }
        dis = heights[i] * dis;
        if (dis > ans) ans = dis;
    }
    return ans;
}
int main()
{
    // RE
    int heights[maxn];
    int n;
    while ( scanf("%d", &n)!=EOF ) {
        for (int i = 0; i < n; i++)
            scanf("%d", &heights[i]);
        printf("%d\n", largestRectangleArea(heights, n));
    }
    return 0;
}

O(n) 解法

对于每个柱子,以它为高度,它往左往右都最多只能延伸到第一个比它矮的柱子。

所以可以维护一个值递增的栈(每遍历完一个就加一个,栈里值为柱子对应下标,但这些柱高是递增的 ),扫描一次柱子,取栈顶,如果 栈顶柱子 比当前柱子高,那以栈顶柱子的高度为根基,最右能延伸到当前柱子,最左能延伸到 栈顶柱子的下一个栈顶(即pop2次,因为栈是递增的),就可以 以栈顶柱子的高度,当前柱子 - 栈顶的下一个栈顶 - 1 为宽度。 然后把当前柱子加到栈里。


怎么保证栈递增?

因为每次把当前元素入栈前,都会弹出栈里比当前高的,所以栈里的元素高度一定是递增的。
如果有某次递减的(比如 5 3 6 的3,这样在3入栈前就把5给弹出并计算了,所以栈里剩3和6,而不会有5和3同时出现)

为了避开 2 1 2 ( 答案是3) 这种比 1 矮的柱子不存在导致没被计算的情况,我们在柱子最前面和最后面都加一个0高度的柱子,这样1这个就能被计算到,高度为1,宽度为 虚拟的4号 - 虚拟的0号 - 1 ,即面积为3。
柱高: 0 2 1 2 0
下标: 0 1 2 3 4

为了避免特判栈为空的情况,给栈也压入一个0元素

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <stack>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define clr( a , x ) memset ( a , x , sizeof (a) );
#define RE freopen("1.in","r",stdin);
const int maxn = 100000 + 5;
int h[maxn];

int main() {
    int n;
    while (scanf("%d",&n)!=EOF,n) {
        for (int i = 1; i <= n; ++i) {
            scanf("%d",&h[i]);
        }
        stack<int>s;
        ll ans = 0;
        s.push(0);
        h[0] = 0;
        h[++n] = 0;
        for (int i = 1; i <= n; ++i) {
            while (h[i] < h[s.top()]) {
                ll height = h[s.top()];s.pop();
                ll width = i - s.top() - 1; //这里的top是经过pop的,栈栈顶元素
                if (width * height > ans) {
                    ans = width * height;
                }
            }
            s.push(i);
        }
        cout << ans << endl;
    }
    return 0;
}

对应LeetCode Hard 84 . Largest Rectangle in Histogram的版本

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        if(n==0) return 0;
        int ans = 0;

        stack<int>s;
        s.push(0);
        heights.insert(heights.begin(),-1);

        heights.push_back(0);
        n++;
        for (int i = 1; i <= n; ++i) {
            while (heights[i] < heights[s.top()]) {
                int a = heights[s.top()];s.pop();
                int b = i - s.top() - 1;
                if (a * b > ans) {
                    ans = a * b;
                }
            }
            s.push(i);
        }
        return ans;
    }
};

二维的情况

POJ 3494 Largest Submatrix of All 1’s | 二维
给一个二维的01矩形,求最多1所围成的矩形面积。

0 0 0 0
0 1 1 0
0 1 1 0
0 0 0 0

我们把二维转为一维,如图我们从最后行看的话等价于

0 2 2 0

这就成一维了,再进一步,我们需要在每行都这么干,然后就ok了。
先对每行的每列求出该列之上1的个数(如上),遍历每一行,按一维的情况去处理就行了。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <stack>
#include <iostream>
#include <algorithm>
using namespace std;
#define clr( a , x ) memset ( a , x , sizeof (a) );
#define RE freopen("1.in","r",stdin);
const int maxn = 2005;

int n,m;
int arr[maxn][maxn],data[maxn][maxn];
int solve(int row){
    stack<int>s;
    int ans = 0;
    s.push(0);
    data[row][0] = 0;
    data[row][m+1] = 0;
    for (int j = 1; j <= m+1; ++j) {
        while (data[row][j] < data[row][s.top()]) {
            int a = data[row][s.top()]; s.pop();
            int b = j - s.top() - 1;
            if (a * b > ans) {
                ans = a * b;
            }
        }
        s.push(j);
    }
    return ans;
}
int main() {

    // RE
    while (scanf("%d%d",&n,&m)!=EOF) {
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j){
                scanf("%d",&arr[i][j]);
            }
        }
        clr(data,0);
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j){
                if(arr[i][j]) data[i][j] = arr[i][j] + data[i-1][j];
                else   data[i][j] = 0;
            }
        }
        int ans =  0;
        for (int i = 1; i <= n; ++i){
            ans = max(ans,solve(i));
        }
        printf("%d\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u012469987/article/details/52173473