最大子矩阵问题——悬线法
最大子矩阵问题——悬线法
最大子矩阵问题
在一个给定的矩阵中,有若干个障碍点,求出不包含障碍点的最大子矩阵。
定义
有效子矩阵
内部不包含任何障碍点且边界与坐标轴平行的子矩形。
极大有效子矩阵
一个有效子矩形,如果不存在包含它且比它大的有效子矩形,就称这个有效子矩形为极大有效子矩形。
最大有效子矩阵
所有有效子矩形中最大的一个(或多个)。
悬线法
顾名思义,就是用一根线来回的晃动,来求出最大的子矩阵。
板子
for(int i=1;i<=n;i++){//初始化
for(int j=1;j<=m;j++){
r[i][j]=l[i][j]=j;
u[i][j]=1;
}
}
for(int i=1;i<=n;i++){//初始向左的数组
for(int j=2;j<=m;j++){
if(满足条件){
l[i][j]=l[i][j-1];
}
}
}
for(int i=1;i<=n;i++){//初始向右的数组
for(int j=m-1;j>=1;j--){
if(满足条件){
r[i][j]=r[i][j+1];
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i>1){//由上方推出状态
if(满足条件){
l[i][j]=max(l[i][j],l[i-1][j]);
r[i][j]=min(r[i][j],r[i-1][j]);
u[i][j]=u[i-1][j]+1;
}
}
int x=r[i][j]-l[i][j]+1;
int y=min(x,u[i][j]);
ans=max(y,ans);
}
}
例题
P4147 玉蟾宫
直接将板子套上就可以了。
#include <bits/stdc++.h>
using namespace std;
const int maxn=1000+50;
int n,m,ans;
char a[maxn][maxn];
int r[maxn][maxn],l[maxn][maxn],u[maxn][maxn];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf(" %c ",&a[i][j]);
r[i][j]=l[i][j]=j;
u[i][j]=1;
}
}
for(int i=1;i<=n;i++){
for(int j=2;j<=m;j++){
if(a[i][j]==a[i][j-1]&&a[i][j]=='F'){
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]==a[i][j+1]&&a[i][j]=='F'){
r[i][j]=r[i][j+1];
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i>1){
if(a[i][j]==a[i-1][j]&&a[i][j]=='F'){
l[i][j]=max(l[i][j],l[i-1][j]);
r[i][j]=min(r[i][j],r[i-1][j]);
u[i][j]=u[i-1][j]+1;
}
}
int x=r[i][j]-l[i][j]+1;
int y=min(x,u[i][j]);
ans=max(x*u[i][j],ans);
}
}
cout<<ans*3<<endl;
return 0;
}
P1578 奶牛浴场
一看数据,3e4,开朴素的二维数组肯定是开不下的。
我们可以换一种思路,直接枚举每个障碍点,分别从左到右,从上往下枚举即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn=5000+50;
int n,m,s;
struct Node{
int x,y;
}a[maxn];
bool cmp1(Node a,Node b){
return a.x<b.x;
}
bool cmp2(Node a,Node b){
return a.y<b.y;
}
int ans=0;
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<=s;i++){
scanf("%d%d",&a[i].x,&a[i].y);
}
a[++s].x=0;a[s].y=0;
a[++s].x=0;a[s].y=m;
a[++s].x=n;a[s].y=0;
a[++s].x=n;a[s].y=m;
sort(a+1,a+s+1,cmp1);
int up,down,v;
for(int i=1;i<=s;i++){
up=0;
down=m;
v=m-a[i].x;
for(int j=i+1;j<=s;j++){
if(v*(down-up)<=ans)break;
ans=max(ans,(down-up)*(a[j].x-a[i].x));
if(a[j].y>=a[i].y){
down=min(down,a[j].y);
}else{
up=max(up,a[j].y);
}
}
}
sort(a+1,a+s+1,cmp2);
int l,r;
for(int i=1;i<=s;i++){
l=0;
r=n;
v=m-a[i].y;
for(int j=i+1;j<=s;j++){
if(v*(r-l)<=ans)break;
ans=max(ans,(r-l)*(a[j].y-a[i].y));
if(a[j].x>=a[i].x){
r=min(r,a[j].x);
}else{
l=max(l,a[j].x);
}
}
}
cout<<ans<<endl;
return 0;
}