题目描述:
题意:
给一个n*m的01矩阵,一个矩阵中如果全是1则它是合理的,求这个矩阵第二大的合理子矩阵。
题解:
按层来考虑,考虑以第i层为底面的时候,对于第j列来说,从
(
i
,
j
)
,
(
i
−
1
,
j
)
.
.
(i,j),(i-1,j)..
(i,j),(i−1,j)..往上的连续h个1组成一个高为h,底面为1的柱子。
那么对于每一个柱子,用单调栈求出以该柱子为高的矩形的最大面积。
但是这样可能会重复计算同一个矩阵的面积,比如两个柱子高度都是3,那这个面积为6的矩阵会被计算两次,这样次大和最大都是6了,但是实际上只有一个面积为6的矩形:
我们可以只用最左边的柱子去算,具体操作就是:对于一个柱子
h
j
h_j
hj,它左边第一个小于它的柱子的位置是
l
l
l,如果
(
l
,
j
)
(l,j)
(l,j)这段区间有一个柱子的高度等于
h
j
h_j
hj,那么
h
j
h_j
hj这个柱子就直接跳过。
还有一个问题就是,需要求的是次大的矩形,这个矩形也可能包含在最大的矩阵之中,所以还要求一下当前最大矩形削掉一行的面积,削掉一列的面积,判断是不是比当前的次大面积要大。
ac代码:
#include<bits/stdc++.h>
using namespace std;
int n, m;
int fi, se;
const int maxn = 1e3 + 50;
int h[maxn];
int flag[maxn];
char str[maxn];
stack<int> s;
int l[maxn], r[maxn];
int main()
{
scanf("%d%d", &n, &m);
h[0] = h[m + 1] = -1;
fi = se = -1;
for(int i = 1; i <= n; ++i){
scanf("%s", str + 1);
for(int j = 1; j <= m; ++j){
if(str[j] == '0') h[j] = 0;
else h[j]++;
}
while(s.size()) s.pop();
s.push(0);
for(int j = 1; j <= m; ++j){
flag[j] = 0;
while(s.size() && h[s.top()] >= h[j]) {
if(h[s.top()] == h[j]) {
flag[j] = 1;
}
s.pop();
}
l[j] = s.top();
s.push(j);
}
while(s.size()) s.pop();
s.push(m+1);
for(int j = m; j > 0; --j){
while(s.size() && h[s.top()] >= h[j]) s.pop();
r[j] = s.top();
s.push(j);
}
for(int j = 1; j <= m; ++j){
if(flag[j]) continue;
int t = h[j]*(r[j] - l[j] - 1);
if(t >= fi){
se = fi;
fi = t;
}
else if(t > se) se = t;
t = max(h[j]*(r[j] - l[j] - 2), (h[j] - 1)*(r[j] - l[j] - 1) ) ;
if(t > se) se = t;
}
}
if(se == -1) cout<<0<<endl;
else cout<<se<<endl;
}
正解的DP待补……