最大子矩阵扫描法和悬线法

Posted 行码棋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最大子矩阵扫描法和悬线法相关的知识,希望对你有一定的参考价值。

  • 博客主页: https://blog.csdn.net/qq_50285142
  • 欢迎点赞👍收藏✨关注❤留言 📝 如有错误,敬请指正
  • 🎈虽然生活很难,但我们也要一直走下去🎈

最大子矩阵问题

在一个给定的 n ∗ m n*m nm矩形网格中有 n n n个障碍点,要找出网格内部不包含任何障碍点,且边界与坐标轴平行的最大子矩形。

扫描法

例题:奶牛浴场

题意:
n ∗ m n*m nm的牧场里有一些产奶点,先要建造一个矩形的浴场,里面不能包含产奶点,产奶点可以在浴场边缘,求建造的最大面积

性质:

1.极大有效子矩形的四条边无法向四边拓展, 也就是说四条边都存在障碍点或与大矩形边界重合
2.存在一个障碍点的矩形中最大有效子矩形一定是极大有效子矩形

算法复杂度: O ( n 2 ) O(n^2) O(n2)

我们先考虑从上到下扫描点:
我们要维护一个左右边界l,r,保证左右边界之间没有障碍点存在
如果访问到的点j的纵坐标大于当前点i的纵坐标,更新右边界,取最小值
如果访问到的点j的纵坐标小于当前点i的纵坐标,更新左边界,取最大值

更新结果:
r e s = m a x ( r e s , ( r − l ) ∗ ( p [ j ] . f i − p [ i ] . f i ) ) res = max(res,(r-l)*(p[j].fi-p[i].fi)) res=max(res,(rl)(p[j].fip[i].fi))为左右边界之间的差乘上访问点j和当前点ix坐标之差,即为更新了 以访问点j和当前点i为上下的边界,以维护的左右l,r为左右边界所组成矩形的面积

然后从左到右扫描所有的点和上面时类似的道理

参考博客

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef pair<int,int> pii;
const int N = 5005;

pii p[N];
int res;
int n,m,k;

int main()
{
	cin>>n>>m>>k;
	for(int i=1;i<=k;i++)
		cin>>p[i].fi>>p[i].se;
	//加入矩形的四个点
	p[++k] = {0,0};
	p[++k] = {0,m};
	p[++k] = {n,0};
	p[++k] = {n,m};
	
	sort(p+1,p+1+k,[](pii a,pii b){
		return a.fi<b.fi;
	});//按x坐标排序
	int l,r,v;//左右边缘
	for(int i=1;i<=k;i++)
	{
		l = 0,r = m,v = n-p[i].fi;
		for(int j=i+1;j<=k;j++)
		{
			if(v * (r-l) < res) break;//剪枝
			res = max(res,(r-l)*(p[j].fi-p[i].fi));
			if(p[j].se >= p[i].se) r = min(r,p[j].se);
			else l = max(l,p[j].se);
		}
	}
	
	sort(p+1,p+1+k,[](pii a,pii b){
		return a.se < b.se;
	});//按y坐标排序
	
	int u,d;//上下边缘
	for(int i=1;i<=k;i++)
	{
		u = 0,d = n,v = m-p[i].se;
		for(int j=i+1;j<=k;j++)
		{
			if(v * (d-u) < res) break;
			res = max(res,(d-u)*(p[j].se-p[i].se));
			if(p[j].fi >= p[i].fi) d = min(d,p[j].fi);
			else u = max(u,p[j].fi);
		}
	}
	cout<<res<<'\\n';
	return 0;
 }

悬线法

例题:最大0矩阵

悬线:该点到上端的最长连续0长度

H [ i , j ] H[i,j] H[i,j]为点 ( i , j ) (i,j) (i,j)对应的悬线的长度。

L [ i , j ] L[i,j] L[i,j]为点 ( i , j ) (i,j) (i,j)对应的悬线向左最多能够移动到的位置。

R [ i , j ] R[i,j] R[i,j]为点 ( i , j ) (i,j) (i,j)对应的悬线向右最多能够移动到的位置。
 
我们要维护每一格对应向上的最大连续0的长度,就是所谓的悬线,还要维护该条悬线向左向右扩展的最远位置的下标,即为上面标明的三个值。

结果计算就是每一格的对应面积 ( r [ i ] [ j ] − l [ i ] [ j ] + 1 ) ∗ h [ i ] [ j ] (r[i][j]-l[i][j]+1)*h[i][j] (r[i][j]l[i][j]+1)h[i][j],所有取一个最大值。

1.维护 h [ i ] [ j ] h[i][j] h[i][j]:

  1. 当前位为0时: h [ i ] [ j ] = h [ i − 1 ] [ j ] + 1 h[i][j] = h[i-1][j]+1 h[i][j]=h[i1][j]+1,上面的一位的长度加一
  2. 当前位为1时: h [ i ] [ j ] = 0 h[i][j]=0 h[i][j]=0,因为初始化就为0,就没有操作

2.维护 l [ i ] [ j ] l[i][j] l[i][j]

需要额外声明一个 l l ll ll变量表示该行左侧 0最远到达的下标 l l ll ll,因为访问该行时,遇见1然后 l l ll ll就会更新为1的右面 j + 1 j+1 j+1的位置,碰见0就和上一行悬线左侧最远到达的位置进行最大更新

  1. 当前位为0时: l [ i ] [ j ] = m a x ( l [ i − 1 ] [ j ] , l l ) l[i][j] = max(l[i-1][j],ll) l[i][j]=max(l[i1][j],ll),使悬线位置最远,即x坐标最大
  2. 当前位为1时: l [ i ] [ j ] = 1 l[i][j]=1 l[i][j]=1,更新 l l ll ll j + 1 j+1 j+1

3.维护 r [ i ] [ j ] r[i][j] r[i][j]

需要从右边维护,道理和上面的相似

  1. 当前位为0时: r [ i ] [ j ] = m i n ( r [ i − 1 ] [ j ] , r r ) r[i][j] = min(r[i-1][j],rr) r[i][j]=min(r[i1][j],rr)
  2. 当前位为1时: r [ i ] [ j ] = n r[i][j]=n r[i][j]=n。n为初始值,保证递推的时候在最右端,如果访问到下面为0的位置的话, r [ i − 1 ] [ j ] r[i-1][j] r[i1][j]代表的就是为1的位,保证 r [ i ] [ j ] = m i n ( r [ i − 1 ] [ j ] , r r ) r[i][j] = min(r[i-1][j],rr) r[i][j]=min(r[i1][j],rr)中的 r [ i − 1 ] [ j ] r[i-1][j] r[i1][j]总是最大的,然后就可以取rr这个值(本行的到达最右端的位置)

维护完就可以计算结果了

另外本题需要使用滚动数组才能过掉,先理解二维,然后压缩成一维就可以了

MLE代码:(重在理解递推)

#include<bits/stdc++.h>
using namespace std;
const int N = 2005;

int a[N][N];
int res,n;
int h[N][N],l[N][N],r[N][N];

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>a[i][j];
	
	for(int i=1;i<=n;i++)
	{
		int ll=1,rr=n;
		for(int j=1;j<=n;j++)
		{
			if(a[i][j]) ll = j+1,l[i][j] = 1;
			else h[i][j] = h[i-1][j]+1,l[i][j]=max(l[i-1][j],ll);
		}
		for(int j=n;j>=1;j--)
		{
			if(a[i][j]) rr = j-1,r[i][j] = n;
			else r[i][j] = min(rr,r[i-1][j]);
			res = max(res,(r[i][j]-l[i][j]+1)*h[i][j]);
		}
	}
	cout<<res<<'\\n';
	return 0;
 }

AC代码(上述代码优化为滚动数组)

#include<bits/stdc++.h>
using namespace std;
const int N = 2005;

int a[N][N];
int res,n;
int h[N],l[N],r[N];

动态规划之悬线法模板(最大子矩阵问题)

悬线法dp

最大子矩阵问题&悬线法小结

悬线法刷题记录

Codevs 1159 最大全0子矩阵 悬线法!!!!

[DP专题]悬线法