Largest Submatrix of All 1’s(单调栈)

题目链接:https://cn.vjudge.net/problem/POJ-3494

Given a m-by-n (0,1)-matrix, of all its submatrices of all 1’s which is the largest? By largest we mean that the submatrix has the most elements.

Input

The input contains multiple test cases. Each test case begins with m and n (1 ≤ m, n ≤ 2000) on line. Then come the elements of a (0,1)-matrix in row-major order on m lines each with n numbers. The input ends once EOF is met.

Output

For each test case, output one line containing the number of elements of the largest submatrix of all 1’s. If the given matrix is of all 0’s, output 0.

Sample Input

2 2
0 0
0 0
4 4
0 0 0 0
0 1 1 0
0 1 1 0
0 0 0 0

Sample Output

0
4

求一个0-1矩阵中全为1的最大子矩阵。
关键是看出最大子矩阵,一定是从某个位置开始,向上连续1到达最高点,然后分别左右延伸得到最大面积,即为最大子矩阵种元素的个数。
因此,先预处理出所有点向上连续1的个数。然后利用单调栈,以这个数为高,每行都是O(n)的左右分别延伸求面积的最大值。这一步就转化为了挑战上的例题:求柱形图中矩形的最大面积。这题难点在于,是二维矩阵,也就是对每一行都用挑战例题中的方法求解。时间复杂度为O(m*n)。

//#include<bits/stdc++.h>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<stack>
using namespace std;
const int N=2005;
int a[N][N];
int h[N][N];
int l[N][N],r[N][N];//左边界,右边界
int m,n;
struct node{
	int h,w;
	node(){}
	node(int hh,int ww){
		h=hh;
		w=ww;
	}
};
void hpre(){
	memset(h,0,sizeof(h));
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			if(a[i][j]==0) h[i][j]=0;
			else h[i][j]=h[i-1][j]+1;
		}
	}
}
int solve(){
	stack<node> s;
	hpre();
	for(int i=1;i<=m;i++){
		while(!s.empty()) s.pop();
		for(int j=1;j<=n;j++){
			while(!s.empty()&&s.top().h>=h[i][j]){
				s.pop();
			}
			if(s.empty()) l[i][j]=1;
			else l[i][j]=s.top().w+1;
			s.push(node(h[i][j],j));
		}
	}
	for(int i=1;i<=m;i++){
		while(!s.empty()) s.pop();
		for(int j=n;j>=1;j--){
			while(!s.empty()&&s.top().h>=h[i][j]){
				s.pop();
			}
			if(s.empty()) r[i][j]=m;
			else r[i][j]=s.top().w-1;
			s.push(node(h[i][j],j));
		}
	}
	int ans=0;
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			ans=max(ans,h[i][j]*(r[i][j]-l[i][j]+1));
		}
	}
	printf("%d\n",ans);
}
int main(){
	while(~scanf("%d%d",&m,&n)){
		for(int i=1;i<=m;i++){
			for(int j=1;j<=n;j++){
				scanf("%d",&a[i][j]);
			} 
		}
		solve();
	}
	return 0;
}

 

也可以先求从某个位置开始向左有多少连续的1,然后分别向上下延伸

#include <iostream>
#include <stack>
#include<cstdio>
#include<cstring>
using namespace std;
int top,tmp,ans,m,n,i,j,a[2010][2010],t[2010];//t数组代表以a[i][j]为边向上能扩展到哪,之前只向下扩展了 
int main()
{
    stack<int> s;
    while(scanf("%d%d",&m,&n)!=EOF){
        ans=0;
        for(i=1;i<=m;i++){
            a[i][0]=0;
            for(j=1;j<=n;j++){
                scanf("%d",&a[i][j]);
                if(a[i][j]&&a[i][j-1])
                    a[i][j]+=a[i][j-1];
            }
        }
        for(j=0;j<=n;j++)
            a[m+1][j]=-1;
        for(j=1;j<=n;j++){
            for(i=1;i<=m+1;i++){
                if(s.empty()||a[s.top()][j]<=a[i][j]){
                	s.push(i);//把行入栈 
                	t[i]=i;
				}     
                else{
                    while(!s.empty()&&a[s.top()][j]>a[i][j]){
                        top=s.top();
                        t[i]=t[top];
                        s.pop();
                        tmp=(i-t[top])*a[top][j];//下限到上限的距离* 边长 
                        if(tmp>ans)
                            ans=tmp;
                        
                    }
                    s.push(i);
                    //a[top][j]=a[i][j]; 没用 
                }
            }
            while(!s.empty())
                s.pop();
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

全部评论

相关推荐

迷茫的大四🐶:如果只是看一下找个高手批一下,或者搞个假的,如果查学信网那就爆了吧
点赞 评论 收藏
分享
喵_coding:年底缺人是短视频营造出来的 而且一般说的也很宽泛 不是特指后端
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务