问题描述:
给定一个N*M的矩阵,其中有一些格子是空地,其他是障碍。
找出一个全部由空地组成的面积/周长最大的子矩阵。
朴素算法:
枚举左上角的坐标O(mn)和右下角的坐标O(mn),判断是否全为空地O(mn),时间复杂度为O(m3*n3)。
扫描线法:
把点(i,j)向上所有连续的空格看做一条悬线。矩阵中的每个点都向上对应了一条悬线。
①用h[i,j]表示点(i,j)对应的悬线长度。
当(i,j)为障碍时,h[i,j]=0;当(i,j)为空格时,h[i,j]=h[i-1,j]+1。
②用left[i,j]表示点(i,j)对应的悬线的左边界。
当(i,j)为障碍时,left[i,j]=左边界;当(i,j)为空格时,left[i,j]=max( left[i-1,j], lo+1 ),lo为格子(i,j)左边最近障碍的列编号。
③用right[i,t]表示点(i,j)对应的悬线的右边界。
当(i,j)为障碍时,right[i,j]=右边界; 当(i,j)为空格时,right[i,j]=min( right[i+1,j], ro-1 ),ro为格子(i,j)右边最近障碍的列编号。
从上到下扫描,对每一行,从左到右计算left[i,j]维护lo;从右到左计算right[i,j]维护ro并更新答案。
在实际实现中,降维节约空间,用h[j],left[j],right[j] 表示当前扫描行的信息。
模板:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1111;
int mat[maxn][maxn];
int n,m;
//返回矩形的最大面积,障碍物代号为c
int cat(int c)
{
int h[maxn],l[maxn],r[maxn];
int lo,ro;
int ans=0;
for (int j=1;j<=m;j++)
{
h[j]=0;
l[j]=1;
r[j]=m;
}
for (int i=1;i<=n;i++)
{
lo=0;ro=m+1;
for (int j=1;j<=m;j++)
{
if (mat[i][j]==c){ h[j]=0;l[j]=1;lo=j; }
else
{
h[j]++;
l[j]=max(l[j],lo+1);
}
}
for (int j=m;j>=1;j--)
{
if (mat[i][j]==c){ r[j]=m;ro=j; }
else
{
r[j]=min(r[j],ro-1);
ans=max(ans,h[j]*(r[j]-l[j]+1));
}
}
}
return ans;
}
题目: