- 博客主页: https://blog.csdn.net/qq_50285142
- 欢迎点赞👍收藏?关注?留言 📝 如有错误,敬请指正
- 🎈虽然生活很难,但我们也要一直走下去🎈
最大子矩阵问题
在一个给定的
n
?
m
n*m
n?m矩形网格中有
n
n
n个障碍点,要找出网格内部不包含任何障碍点,且边界与坐标轴平行的最大子矩形。
扫描法
例题:奶牛浴场
题意:
n
?
m
n*m
n?m的牧场里有一些产奶点,先要建造一个矩形的浴场,里面不能包含产奶点,产奶点可以在浴场边缘,求建造的最大面积
性质:
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,(r?l)?(p[j].fi?p[i].fi))为左右边界之间的差乘上访问点j 和当前点i 的x 坐标之差,即为更新了 以访问点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;
});
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;
});
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]:
- 当前位为0时:
h
[
i
]
[
j
]
=
h
[
i
?
1
]
[
j
]
+
1
h[i][j] = h[i-1][j]+1
h[i][j]=h[i?1][j]+1,上面的一位的长度加一
- 当前位为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就和上一行悬线左侧最远到达的位置进行最大更新
- 当前位为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[i?1][j],ll),使悬线位置最远,即x坐标最大
- 当前位为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]
需要从右边维护,道理和上面的相似
- 当前位为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[i?1][j],rr)
- 当前位为1时:
r
[
i
]
[
j
]
=
n
r[i][j]=n
r[i][j]=n。n为初始值,保证递推的时候在最右端,如果访问到下面为0的位置的话,
r
[
i
?
1
]
[
j
]
r[i-1][j]
r[i?1][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[i?1][j],rr)中的
r
[
i
?
1
]
[
j
]
r[i-1][j]
r[i?1][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];
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[j] = 1,h[j]=0;
else h[j]++,l[j]=max(l[j],ll);
}
for(int j=n;j>=1;j--)
{
if(a[i][j]) rr = j-1,r[j] = n;
else r[j] = min(rr,r[j]);
res = max(res,(r[j]-l[j]+1)*h[j]);
}
}
cout<<res<<'\n';
return 0;
}
往期优质文章推荐
|