问题描述
现有 H × W H×W H×W个边长为1cm的正方形瓷砖排列在一起,其中有一部分瓷砖沾有污迹,求仅由干净瓷砖构成的最大长方形的面积。
输入:
H
H
H
W
W
W
c
1
,
1
c_{1,1}
c1,1
c
1
,
2
c_{1,2}
c1,2 …
c
1
,
W
c_{1,W}
c1,W
c
2
,
1
c_{2,1}
c2,1
c
2
,
2
c_{2,2}
c2,2 …
c
2
,
W
c_{2,W}
c2,W
…
c
H
,
1
c_{H,1}
cH,1
c
H
,
2
c_{H,2}
cH,2 …
c
H
,
W
c_{H,W}
cH,W
第1行输入2个整数H、W,用空格隔开。接下来H行输入H×W个代表瓷砖的整数
c
i
j
c_{ij}
cij。
c
i
j
c_{ij}
cij为1表示瓷砖沾有污渍,为0表示干净
输出:
输出面积的最大值,占1行
限制:
1 ≤ H,W ≤ 1400
输入示例
4 5
0 0 1 0 0
1 0 0 0 0
0 0 0 1 0
0 0 0 1 0
输入示例
6
讲解
我们求解正方形时(参见:动态规划 | 最大正方形 | Largest Square | C/C++实现)所用的算法在这道题中并不适用,需另寻他法。
首先,建立一个二维数组T[MAX][MAX],在表T中记录各元素向上存在多少个连续的干净瓷砖。对各列使用动态规划法可以很轻松的求出T
我们把表T的每行都看成一个直方图,本题就成了求直方图内最大长方形的问题。于是我们转为考虑求直方图中最大长方形的面积。这里最容易想到的是穷举法,我们可以列出直方图的所有端点,求出各个范围内的最大长方形的面积(以该范围内最小值为高的长方形的面积),然后取其中最大值。但是此法整体的复杂度高达 O ( H W 2 ) O(HW^2) O(HW2)或 O ( H 2 W ) O(H^2W) O(H2W),不够理想
其实在解决这个问题时,只要用栈替代数组记录局部问题的解,就能大幅提高求最优解的效率。栈中记录”仍有可能扩张的长方形的信息(记为rect)“。rect内含有两个信息,一个是长方形的高height,另一个是其左端的位置pos。首先我们将栈置空,接下来对于直方图的各个值
H
i
(
i
=
0
,
1
,
.
.
.
,
W
−
1
)
H_i(i=0,1,...,W-1)
Hi(i=0,1,...,W−1),创建以
H
i
H_i
Hi为高,以其下标 i 为左端位置的长方形rect,然后进行以下处理:
1.如果栈为空:
将rect压入栈。
2.如果栈顶长方形的高小于rect的高:
将rect压入栈。
3.如果栈顶长方形的高等于rect的高:
不作处理。
4.如果栈顶长方形的高大于rect的高:
只要栈不为空,且栈顶长方形的高大于等于rect的高,就从栈中取出长方形,同时计算其面积并更新最大值。长方形的长等于”当前位置 i”与之前记录的“左端位置pos”的差值
将rect压入栈。另外,这个rect的左端位置pos为最后从栈中取出的长方形的pos值。
处理各直方图时,向栈添加或删除长方形的操作需要消耗 O ( W ) O(W) O(W)。搜索长方形的问题要对每一行执行一次这种处理,复杂度为 O ( H W ) O(HW) O(HW)。
AC代码如下
#include<stdio.h>
#include<iostream>
#include<stack>
#include<algorithm>
#define MAX 1400
using namespace std;
struct Rectangle { int height; int pos; };
int getLargestRectangle(int size, int buffer[]) {
stack<Rectangle> S;
int maxv = 0;
buffer[size] = 0;
for(int i = 0; i <= size; i++) {
Rectangle rect;
rect.height = buffer[i];
rect.pos = i;
if(S.empty() ) {
S.push(rect);
} else {
if(S.top().height < rect.height) {
S.push(rect);
} else if(S.top().height > rect.height) {
int target = i;
while(!S.empty() && S.top().height >= rect.height) {
Rectangle pre = S.top(); S.pop();
int area = pre.height * (i-pre.pos);
maxv = max(maxv, area);
target = pre.pos;
}
rect.pos = target;
S.push(rect);
}
}
}
return maxv;
}
int H, W;
int buffer[MAX][MAX];
int T[MAX][MAX];
int getLargestRectangle() {
for(int j = 0; j < W; j++) {
for(int i = 0; i < H; i++) {
if(buffer[i][j]) {
T[i][j] = 0;
} else {
T[i][j] = (i > 0) ? T[i-1][j] + 1 : 1;
}
}
}
int maxv = 0;
for(int i = 0; i < H; i++) {
maxv = max(maxv, getLargestRectangle(W, T[i]));
}
return maxv;
}
int main(){
scanf("%d %d", &H, &W);
for(int i = 0; i < H; i++) {
for(int j = 0; j < W; j++) {
scanf("%d", &buffer[i][j]);
}
}
cout<<getLargestRectangle()<<endl;
return 0;
}