题目大意:
给定一个n*m的只含0 1的矩阵,求第二大全1的矩阵面积
输入样例
1 2
01
输出
0
输入
1 3
101
输出
1
求第二大的全1矩阵,首先要确定怎么求每个矩阵,我们确定每一列上的1,如果当前列有1,那么我们把下面一行的对应列上的1+1;如
0001
0011
0011
0000
就可以预先处理成
0001
0012
0023
0000
每个数字表示这之上还有几个1,这样过会就可以对列进行排序,从而找到1最多的那个列,因为是最多的列1先找到,那么剩下相邻列如果也有1,那么一定比这列小,这样总是可以组成小的列上的矩形,长度用并查集来维护,这样每次相邻的位置都可以找到长度能够到最大延申的位置;然后我们需要找到第二大的矩形,那么第二大满足矩形的什么条件呢,事实上我们只需要找到各个不在同一连通里矩形和在每个连通里矩形外围扣一个点,这样就会变成找每个独立连通的矩形和这个矩形的长-1或者宽-1,只保存最大和第二大,最后输出第二大即可。
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define F first
#define S second
typedef pair<int,int> p;
int n,m,maxa,maxb;
char s[1010];
int a[1010][1010],t[1010][1010];
int vis[1010],sz[1010],pre[1010];
p h[1010];
void read(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
cin >> s+1;
for(int j=1;j<=m;j++)
a[i][j]=(s[j]=='0')?0:1;
}
}
void work1(){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
t[i][j]=(a[i][j]==1)?t[i-1][j]+1:0; //预处理每列1
}
}
}
int ffind(int x){
return x==pre[x]?x:pre[x]=ffind(pre[x]); //并查集维护最大长度
}
void _union(int x,int y){
pre[y]=x;
sz[x]+=sz[y];
}
void work(int x){ //只保存最大和第二大
if(x>maxa) maxb=maxa,maxa=x;
else if(x>maxb) maxb = x;
}
int main(){
read();
work1();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) pre[j]=j,sz[j]=1,vis[j]=0,h[j]=(p){t[i][j],j};
sort(h+1,h+m+1);
for(int j=m;j;j--){
int x=h[j].S;
vis[x]=1;
if(vis[x-1]) _union(ffind(x),ffind(x-1));//如果当前大小点的左右两边有之前被遍历过的,那么就满足大边必定满足小边
if(vis[x+1]) _union(ffind(x),ffind(x+1));
int len = sz[ffind(x)];
work(len*h[j].F); //这里找第二大的,只需要扣一个点下来即可, xy,x(y-1),(x-1)y
work(len*(h[j].F-1));
work((len-1)*h[j].F);
}
}
cout<<maxb<<endl;
return 0;
}