学习笔记 悬线法
-
最大子矩阵问题:
在一个给定的矩形中有一些障碍点,找出内部不包含障碍点的,边与整个矩形平行或重合的最大子矩形。
极大子矩型:无法再向外拓展的有效子矩形
最大子矩型:最大的一个有效子矩形
特别的,在一个有障碍点的矩形中,最大子矩形一定是极大子矩形
-
悬线法
悬线:上端覆盖了一个障碍点或者到达整个矩形上边界的有效线段
每个悬线上的点的与底部的点一一对应,矩形中每一个点(矩形顶部点除外)都对应了一条悬线。
如果把一条悬线向左右两个方向尽可能的移动,那么就得到了一个矩形。
注意:悬线对应的矩型不一定是极大子矩阵,因为悬线定义中固定了悬线的下边界,故而,悬线左右移动所得到的矩形无法向下扩展。
-
悬线法的实现
三个重要的元素:
- heighti,j :表示以( i ,j )为底的悬线的高 (初始化为1)
- lefti,j :表示向左最多能移动到的位置 (初始化为j)
- righti,j : 表示向右最多能移动到的位置 (初始化为j)
其中的left,right要视题目要求进行进一步的初始化,如例题
转移:
如果点 不是障碍点,那么,以 为底的悬线就等于以 为底的悬线加点 到点 的线段。因此, 。
当然还要注意左右边界的问题
以上图片转自 https://zhuanlan.zhihu.com/p/46382722
画个图理解一下
那么计算面积就轻而易举
对于以点 为底的悬线对应的子矩形,其面积计算为
问题解:
时间复杂度: ;空间复杂度:
例题:luogu P1169 棋盘制作
直接上代码
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 #define maxn 2010 5 #define re register 6 using namespace std; 7 int n,m,ans1,ans2; 8 int map[maxn][maxn],height[maxn][maxn]; 9 int l[maxn][maxn],r[maxn][maxn]; 10 int main() 11 { 12 scanf("%d%d",&n,&m); 13 for(re int i=1;i<=n;++i) 14 for(re int j=1;j<=m;++j) 15 { 16 scanf("%d",&map[i][j]); 17 height[i][j]=1; 18 l[i][j]=r[i][j]=j; 19 } 20 for(re int i=1;i<=n;++i) 21 for(re int j=2;j<=m;++j) 22 { 23 if(map[i][j]!=map[i][j-1]) 24 l[i][j]=l[i][j-1]; 25 } 26 for(re int i=1;i<=n;++i) 27 for(re int j=m-1;j>=1;j--) 28 { 29 if(map[i][j]!=map[i][j+1]) 30 r[i][j]=r[i][j+1]; 31 } 32 //以上为初始化 33 for(re int i=1;i<=n;++i) 34 for(re int j=1;j<=m;++j) 35 { 36 if(i>1&&map[i][j]!=map[i-1][j]) 37 { 38 height[i][j]=height[i-1][j]+1; 39 l[i][j]=max(l[i][j],l[i-1][j]); 40 r[i][j]=min(r[i][j],r[i-1][j]); 41 } 42 int a=r[i][j]-l[i][j]+1; 43 int b=min(height[i][j],a); 44 ans1=max(ans1,a*height[i][j]);//最大矩形 45 ans2=max(ans2,b*b);//最大正方形 46 } 47 printf("%d %d",ans2,ans1); 48 return 0; 49 }
注:部分内容转载自
Flavius Buffon:悬线法用来求解最大子矩形问题 同时也是参考文献