扫描线法求最大子矩阵
问题描述:
给定一个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; }
题目: