悬线法 学习笔记
之前的考试包括做题都有用过这个方法,算是个套路了。写一篇博客总结一下。
----------------
悬线法的用途
针对求给定矩阵中满足某条件的极大矩阵,比如“面积最大的长方形、正方形”“周长最长的矩形等等”。可以满足在时间复杂度为O(M*N)的要求,比一般的枚举高效的多,也易于理解。
悬线法的思路
悬线法,悬线的定义,就是一条竖线,这条竖线要满足上端点在整个矩形上边界或者是一个障碍点。然后以这条悬线进行左右移动,直到移至障碍点或者是矩阵边界,进而确定这条悬线所在的极大矩阵。也就是说,我们要针对矩阵中每个点进行求极大矩阵的操作。所以我们要$left$数组来存每个点能到达的最左的地方,$right$数组存每个点能到达的最右的地方。同时设置一个$up$数组表示最上能到达的地方,用来存矩形的上边界。有转移:
$r[i][j]=r[i][j+1](a[i][j]==a[i][j+1])$
$l[i][j]=l[i][j-1](a[i][j]==a[i][j-1])$
$up[i][j]=up[i-1][j]+1(a[i][j]==a[i-1][j])$
同时,在$up$进行转移的同时$left$和$right$要进行更新。
例题
1.棋盘制作
裸题了。直接上套路即可。
代码:
#include<cstdio> #include<iostream> using namespace std; const int N=2005; int l[N][N],r[N][N],up[N][N],a[N][N],n,m,ans1,ans2; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } int main() { n=read();m=read(); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { a[i][j]=read(); l[i][j]=r[i][j]=j; up[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]) 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]) r[i][j]=r[i][j+1]; for (int i=2;i<=n;i++) for (int j=1;j<=m;j++) { if (a[i][j]!=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]); up[i][j]=up[i-1][j]+1; } int a=r[i][j]-l[i][j]+1; int b=min(a,up[i][j]); ans1=max(ans1,b*b); ans2=max(ans2,a*up[i][j]); } printf("%d\n%d",ans1,ans2); return 0; }
2.奶牛浴场
这题在洛谷的数据加强后好多题解都被hack掉了。连wzk大佬论文里面的代码也有bug233。
大体的思想是按照横坐标排序后选取两个点作为矩形的两个角计算面积,同时更新上下边界。从左到右扫完后从右到左再扫一遍。最后还有按照纵坐标排序再扫一遍。证明可以看洛谷第二篇题解。
代码:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N=5050; int l,w,n,ans; struct node { int x,y; }a[N]; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } bool cmp1(node x,node y) { return x.x<y.x; } bool cmp2(node x,node y) { return x.y<y.y; } int main() { l=read();w=read();n=read(); for (int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(); a[++n].x=0;a[n].y=0; a[++n].x=0;a[n].y=w; a[++n].x=l;a[n].y=0; a[++n].x=l;a[n].y=w; int x1,y1,x2,y2; sort(a+1,a+n+1,cmp1); for (int i=1;i<=n;i++) { x1=a[i].x,y1=0,y2=w; for (int j=i+1;j<=n;j++) { x2=a[j].x; ans=max(ans,(x2-x1)*(y2-y1)); if (a[j].y<a[i].y) y1=max(y1,a[j].y); else y2=min(y2,a[j].y); } } for (int i=n;i>=1;i--) { x1=a[i].x,y1=0,y2=w; for (int j=i-1;j>=1;j--) { x2=a[j].x; ans=max(ans,(x1-x2)*(y2-y1)); if (a[j].y<a[i].y) y1=max(y1,a[j].y); else y2=min(y2,a[j].y); } } sort(a+1,a+n+1,cmp2); for (int i=1;i<n;i++) ans=max(ans,l*(a[i+1].y-a[i].y)); printf("%d",ans); return 0; }