原题:POJ 3494
题意:
给出一个矩阵,求全1子矩阵的最大面积
解析:
开局的处理方式和最大求和子矩阵类似,压缩处理。
预处理h[i][j],表示第i行第j列往上(行数递减方向)可以接上的全1串的最长长度,然后处理第一行到第i行的ans时,就可以看成处理h[i]一行了
eg:
n=3 m=4
M数组 H数组
0 1 1 0 0 1 1 0
1 1 1 1 --> 1 2 2 1
1 0 1 0 2 0 3 0
接下来,对于每一行该怎么处理?
最大面积一定是某一个点的高 * 往左右延大于等于这个高的长度,所以只要对于每个高,处理出其可以延伸的左端点和右端点,ans=max(ans,H*len)
,用单调栈可以在n的时间内得到所有高的len
单调栈:
假设H数组如下
维护一个单调不减的栈,首先s[0]=0,h[1]=1>0,入栈s[1]=1(下标),h[2]>s[1],入栈s[2]=2,同理s[3]=3
这个时候,每个入栈的都比前面的大,即当前为最大,故当前高度的左端点就是前面那个的下标,有 L[1]=0,L[2]=1,L[3]=2
(若i可以往左延伸到i-j,L[i]==i-j-1,同理R[i]==i+j+1)
如果将要入栈的数比栈顶元素小,那么说明两点:
- 对于栈顶的那个下标,已经延伸不到当前点了,就可以确定下其右端点R了
- 对于入栈元素,栈顶下标的高度可以让其延伸,那么就使其出栈,直到延伸不到栈顶元素为止,就可以得到将要入栈下标的左端点L
eg:
h[4]=2,比s[3]小,所以s[3]出栈,出栈的时候,s[3]为3,即第3个数不能延伸到4了,所以R[3]=4,这个时候,下标为3高度为3的左右端点已经得出,ans=max(ans,3*(4-2-1))
至于为什么相同的不出栈
其实出不出都可以,就是写法上的区别
首先,同一段上的相同高度,只要有一个正确,就可以了
eg:h数组 3 2 4 2 3 那么len[2]=1~5,len[4]=1~5,其中就算有一个是变小了,也不会影响ans=2*5=10
假设不出栈,h[s[2]]==h[4]==2,本来这个点是可以延伸到的,但是我们不出栈,所以第二个数的L不准确了,但是第一个数的R准确了,那么第一个数因为遇到不相同的出栈了,得到的就是准确的
如果出栈的话,前面那个点的R就不准确了,但是接下来的相同的数的L是准确的,只要遇到一个不相同的使任意一个出栈,那么最后一个相同的数的R就是准确的了,因此得到的还是一段准确的区间
代码:
#define N 2009
int n,m;
int M[N][N];
int h[N][N];
int ans;
int s[N],L[N],R[N];
void fin(int row){//相同不出栈
s[0]=0;int top=0;
h[row][m+1]=0;//用于得到最后没出栈元素的R
for(int i=1;i<=m+1;i++){
int ar=s[top];
while(h[row][i]<h[row][ar]){
R[ar]=i;top--;
ar=s[top];
}
L[i]=ar;s[++top]=i;
}
for(int i=1;i<=m;i++)
if(h[row][i])
ans=max(ans,h[row][i]*(R[i]-L[i]-1));
}
void fin_(int row){//相同出栈
s[0]=0;int top=0;
h[row][m+1]=0;
for(int i=1;i<=m+1;i++){
int ar=s[top];
while(h[row][i]<=h[row][ar]){
R[ar]=i;top--;
if(top<0)break;//最后m+1的时候把s[0]也出栈了
ar=s[top];
}
L[i]=ar;s[++top]=i;
}
for(int i=1;i<=m;i++)
if(h[row][i])
ans=max(ans,h[row][i]*(R[i]-L[i]-1));
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
mmm(h,0);ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&M[i][j]);
if(M[i][j])h[i][j]=h[i-1][j]+1;
}
}
for(int i=1;i<=n;i++)fin(i);
printf("%d\n",ans);
}
}
/*
4 4
0 1 1 1
1 0 1 1
1 1 1 1
1 1 1 0
*/