一、最大子矩阵是什么
在一个给定的矩形网格中有一些障碍点,要找出网格内部不包含任何障碍点,且边界与坐标轴平行的最大子矩形。
二、概念解释
1. 有效子矩形
内部不包含任何障碍点且边界与坐标轴平行的子矩形。
2. 极大有效子矩形
一个有效子矩形,如果不存在包含它且比它大的有效子矩形,就称这个有效子矩形为极大有效子矩形,简称极大子矩形。
三、定理
1. 一个最大子矩形一定是一个极大子矩形。
如果这个最大子矩形不是极大子矩形,那么还有包含它且比它大的有效子矩形,与它是最大子矩形矛盾。
2. 一个极大子矩形四边一定不能继续向外扩展。
这是显而易见的。
四、实现方法
1. 悬线法

如上图所示,两个黑色点之间的即为一条悬线,那么我们只需要枚举这一条悬线能到达的左边界与右边界,即可得到一个极大子矩阵的面积。
直接枚举每两个点之间是否可以连成悬线是不可行的,所以不如用
(1). 的状态转移方程。
首先初始时,每个点都可以视为一个悬线,即
不难发现,如果这个点是一个障碍点,那么
(2). 与 的状态转移方程。
首先初始时,将每个点都视为一根悬线。由于不知道他能往左或往右走到哪里,所以赋初值为
然后先看每个点往左和往右最多能走到哪里,即:
最后再处理每根长度大于
同理可得,
最后算出来每个极大子矩阵的面积,输出答案即可,时间复杂度为
(3). 例题
| 平台 | 网址 |
|---|---|
| 洛谷 | https://www.luogu.com.cn/problem/P4147 |
AC Code:
#include <bits/stdc++.h>
#define int long long
using namespace std;
void fread(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
const int N=1e3+10,oo=0x7fffffff;
int n,m,cnt,a[N][N],h[N][N],l[N][N],r[N][N],ans;
signed main(){
fread();
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
char c;
cin>>c;
if(c=='R'){
a[i][j]=1;
}
else cnt++;
l[i][j]=r[i][j]=j;
h[i][j]=1;
}
}
if(!cnt){
cout<<0;
return 0;
}
for(int i=1;i<=n;i++){
for(int j=2;j<=m;j++){
if(!a[i][j] and !a[i][j-1]){
l[i][j]=l[i][j-1];
}
}
}
for(int i=1;i<=n;i++){
for(int j=m-1;j>=1;j--){
if(!a[i][j] and !a[i][j+1]){
r[i][j]=r[i][j+1];
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i>1 and !a[i][j] and !a[i-1][j]){
l[i][j]=max(l[i][j],l[i-1][j]);
r[i][j]=min(r[i][j],r[i-1][j]);
h[i][j]=h[i-1][j]+1;
}
ans=max(ans,(r[i][j]-l[i][j]+1)*h[i][j]);
}
}
cout<<ans*3;
return 0;
}2. 枚举法
(1). 步骤
此枚举非彼枚举,时间复杂度可达到
这个枚举法主要步骤有以下几步:
将障碍点按
坐标从小到大排序。 枚举每个障碍点
作为起点。 用
表示当前极大子矩阵 坐标的上界,用 表示当前极大子矩阵 坐标的下界。 往后枚举每一个障碍点
,记录以 为长, 为宽的矩形的面积,并更新 与 。 将障碍点按
坐标从大到小排序,仿照第 步再做一次二重循环。 将障碍点按
坐标从小到大排序,枚举相邻两个障碍点之间的矩形的面积。
图解:



(2). 例题
| 平台 | 网址 |
|---|---|
| 洛谷 | https://www.luogu.com.cn/problem/P1578 |
AC Code:
#include <bits/stdc++.h>
#define int long long
using namespace std;
void fread(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
const int N=1e3+10,oo=0x7fffffff;
int n,m,cnt,f[N][N],ans;
struct A{
int x,y;
}a[N*N];
bool cmp1(A x,A y){
return x.x<y.x;
}
bool cmp2(A x,A y){
return x.x>y.x;
}
bool cmp3(A x,A y){
return x.y<y.y;
}
signed main(){
fread();
cin>>n>>m>>cnt;
if(!cnt){
cout<<n*m;
return 0;
}
for(int i=1;i<=cnt;i++){
int x,y;
cin>>x>>y;
a[i]={x,y};
}
a[++cnt].x=0,a[cnt].y=0;
a[++cnt].x=0,a[cnt].y=m;
a[++cnt].x=n,a[cnt].y=0;
a[++cnt].x=n,a[cnt].y=m;
sort(a+1,a+cnt+1,cmp1);
for(int i=1;i<=cnt;i++){
int up=m,down=0;
int d1,d2,s;
for(int j=i+1;j<=cnt;j++){
d1=max((int)(0),a[j].x-a[i].x);
d2=max((int)(0),up-down);
s=d1*d2;
ans=max(ans,s);
if(a[j].y>=a[i].y)up=min(up,a[j].y);
if(a[j].y<=a[i].y)down=max(down,a[j].y);
}
}
sort(a+1,a+cnt+1,cmp2);
for(int i=1;i<=cnt;i++){
int up=m,down=0;
int d1,d2,s;
for(int j=i+1;j<=cnt;j++){
d1=max((int)(0),a[i].x-a[j].x);
d2=max((int)(0),up-down);
s=d1*d2;
ans=max(ans,s);
if(a[j].y>=a[i].y)up=min(up,a[j].y);
if(a[j].y<=a[i].y)down=max(down,a[j].y);
}
}
sort(a+1,a+cnt+1,cmp3);
for(int i=1;i<cnt;i++){
ans=max(ans,n*(a[i+1].y-a[i].y));
}
cout<<ans<<endl;
return 0;
}