前言
- 悬线法是一种很好的,可以解决下面这类题型
- 从矩阵中找到一个满足某条件的最大矩阵
- 其思想是极大子矩阵。
悬线法直接搞
套路
悬线如下:
状态表示: 定义
h
[
i
]
[
j
]
h[i][j]
h[i][j] 为从
(
i
,
j
)
(i,j)
(i,j) 的上面挂到
(
i
,
j
)
(i,j)
(i,j) 的一条悬线最长长度,定义
l
[
i
]
[
j
]
,
r
[
i
]
[
j
]
l[i][j],r[i][j]
l[i][j],r[i][j] 为从这条悬线能往左/右延申到的左右边界。
状态转移:
设
t
m
p
tmp
tmp 为
(
i
,
j
)
(i,j)
(i,j) 能到的最左边,若一条悬线能从
(
i
,
j
)
(i,j)
(i,j) 的上面挂到
(
i
,
j
)
(i,j)
(i,j) ,则:
l
[
i
]
[
j
]
=
m
a
x
(
t
m
p
,
l
[
i
−
1
]
[
j
]
)
l[i][j]=max(tmp,l[i-1][j])
l[i][j]=max(tmp,l[i−1][j])
设
t
m
p
tmp
tmp 为
(
i
,
j
)
(i,j)
(i,j) 能到的最右边,若一条悬线能从
(
i
,
j
)
(i,j)
(i,j) 的上面挂到
(
i
,
j
)
(i,j)
(i,j),则:
r
[
i
]
[
j
]
=
m
i
n
(
t
m
p
,
r
[
i
−
1
]
[
j
]
)
r[i][j]=min(tmp,r[i-1][j])
r[i][j]=min(tmp,r[i−1][j])
若一条悬线能从
(
i
,
j
)
(i,j)
(i,j) 的上面挂到
(
i
,
j
)
(i,j)
(i,j),
h
[
i
]
[
j
]
=
h
[
i
−
1
]
[
j
]
h[i][j]=h[i-1][j]
h[i][j]=h[i−1][j]
若不能,考虑是否能从
(
i
,
j
)
(i,j)
(i,j) 令其一条直线,或不能。
初始值: 若无法有一条到达
(
i
,
j
)
(i,j)
(i,j) 的悬线也无法从
(
i
,
j
)
(i,j)
(i,j) 令其一条悬线,则
h
[
i
]
[
j
]
=
0
,
l
[
i
]
[
j
]
=
1
,
r
[
i
]
[
j
]
=
m
h[i][j]=0,l[i][j]=1,r[i][j]=m
h[i][j]=0,l[i][j]=1,r[i][j]=m。
h
[
0
]
[
j
]
=
0
,
l
[
0
]
[
j
]
=
1
,
r
[
0
]
[
j
]
=
m
h[0][j]=0,l[0][j]=1,r[0][j]=m
h[0][j]=0,l[0][j]=1,r[0][j]=m
维护方式: 不同的题有不同的维护方式,但都是维护
h
,
l
,
r
h,l,r
h,l,r 这三个东西。
例题1:最大只包含1的矩形
- 题目描述: 一个 n × m n\times m n×m 的矩阵,每个位置有 F 或 R 。找到一个只包含 F 的最大矩阵。输出该最大矩形的面积 × 3 \times 3 ×3。n,m<=1000
#include<bits/stdc++.h>
using namespace std;
int h[2005][2005],l[2005][2005],r[2005][2005];
char a[2005][2005];
int main() {
int n,m,ans=0;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)cin>>a[i][j];
}
for(int j=1;j<=m;j++){
l[0][j]=1;
r[0][j]=m;
h[0][j]=0;
}
for(int i=1; i<=n; i++) {
int tmp=1;
for(int j=1; j<=m; j++) {
if(a[i][j]=='F') {
h[i][j]=h[i-1][j]+1;
l[i][j]=max(tmp,l[i-1][j]);
}else{
h[i][j]=0;
tmp=j+1;
l[i][j]=1;
}
}
tmp=m;
for(int j=m;j>=1;j--){
if(a[i][j]=='F'){
r[i][j]=min(tmp,r[i-1][j]);
}else{
tmp=j-1;
r[i][j]=m;
}
}
for(int j=1;j<=m;j++)ans=max(ans,h[i][j]*(r[i][j]-l[i][j]+1));
}
cout<<ans*3;
return 0;
}
例题2:最大只包含1矩阵(0可以在边界)
- 题目描述: 一个只含有0和1 n × m n\times m n×m 的矩阵,找到一个只包含 1 的最大子矩阵(0可以在边界上)。输出该最大矩形的面积 。n,m<=2000
#include<bits/stdc++.h>
using namespace std;
int h[2005][2005],l[2005][2005],r[2005][2005],a[2005][2005];
int main() {
int n,m,ans=0;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)cin>>a[i][j];
}
for(int j=1;j<=m;j++){
l[0][j]=1;
r[0][j]=m;
h[0][j]=0;
}
for(int i=1; i<=n; i++) {
int tmp=1;
for(int j=1; j<=m; j++) {
if(a[i][j]==1) {
h[i][j]=h[i-1][j]+1;
l[i][j]=max(tmp,l[i-1][j]);
}else{
h[i][j]=0;
tmp=j;
l[i][j]=1;
}
}
tmp=m;
for(int j=m;j>=1;j--){
if(a[i][j]==1){
r[i][j]=min(tmp,r[i-1][j]);
}else{
tmp=j;
r[i][j]=m;
}
}
for(int j=1;j<=m;j++)ans=max(ans,h[i][j]*(r[i][j]-l[i][j]+1));
}
cout<<ans;
return 0;
}
例题3:最大的01交错矩阵
- 题目描述: 在一个 n × m n\times m n×m 的只包含 0 和 1 的矩阵里找出一个只包含 01 交错的最大正方形和最大矩形矩形,输出该最大正方形和最大矩形的面积。n,m<=2000
- 问题分析: 与上题转移方程基本类似,只不过 t m p tmp tmp 的和悬线高度的维护方式有一点点差异。
- 【注意】正方形 = m a x ( m i n ( h [ i ] [ j ] , r [ i ] [ j ] − l [ i ] [ j ] + 1 ) × m i n ( h [ i ] [ j ] , r [ i ] [ j ] − l [ i ] [ j ] + 1 ) ) max(min(h[i][j],r[i][j]-l[i][j]+1)\times min(h[i][j],r[i][j]-l[i][j]+1)) max(min(h[i][j],r[i][j]−l[i][j]+1)×min(h[i][j],r[i][j]−l[i][j]+1))
#include<bits/stdc++.h>
using namespace std;
int h[2005][2005],l[2005][2005],r[2005][2005],a[2005][2005];
int main() {
int n,m,ans1=0,ans2=0;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)cin>>a[i][j];
}
for(int j=1;j<=m;j++){
l[0][j]=1;
r[0][j]=m;
h[0][j]=0;
a[0][j]=-1;
}
for(int i=1;i<=n;i++)a[i][0]=a[i][m+1]=-1;
for(int i=1; i<=n; i++) {
int tmp=1;
for(int j=1; j<=m; j++) {
if(a[i][j]==a[i][j-1])tmp=j;
if(a[i][j]!=a[i-1][j]) {
h[i][j]=h[i-1][j]+1;
l[i][j]=max(tmp,l[i-1][j]);
}else{
h[i][j]=1;
l[i][j]=tmp;
}
}
tmp=m;
for(int j=m;j>=1;j--){
if(a[i][j]==a[i][j+1])tmp=j;
if(a[i][j]!=a[i-1][j]) {
r[i][j]=min(tmp,r[i-1][j]);
}else{
h[i][j]=1;
r[i][j]=tmp;
}
}
for(int j=1;j<=m;j++)ans1=max(ans1,min(h[i][j],(r[i][j]-l[i][j]+1))*min(h[i][j],(r[i][j]-l[i][j]+1)));
for(int j=1;j<=m;j++)ans2=max(ans2,h[i][j]*(r[i][j]-l[i][j]+1));
}
cout<<ans1<<endl<<ans2;
return 0;
}
例题4:和为某范围之内的矩阵
极大子矩阵思想
例题1:最大的只包含可行点的矩阵
- 题目描述: 一个 n × m n\times m n×m 的矩阵,有 q 个非可行点。找到一个只包含可行点的最大子矩阵(非可行点可以在边界上)。输出该最大矩形的面积 。n,m<=30000,q<=5000。
- 问题分析: n 与 m 过大,显然无法用传统的悬线法来做了。
学习链接
例题4:满足某条件的矩阵数量
- 题目描述: 在一个 n × m n\times m n×m 的矩阵中有 q 个点,问有多少个连续子矩阵至少包含 k 个点。n,m<=3e3,k<=10。
- 问题分析: