1. 程式人生 > >POJ.2559[leetcode.84]直方圖最大矩形及二維情況

POJ.2559[leetcode.84]直方圖最大矩形及二維情況

暴力 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;
}
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;
    }
};

二維的情況

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;
}