最大子矩阵
给出一个
单调栈(悬线法)
扫描整个矩阵,时间复杂度为
例题
P4147 玉蟾宫
考虑以每一行为底边的矩形。用
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m,stk[N],tt,h[N],l[N],r[N];
char a[N][N];
int main(){
cin>>n>>m;
int ans=0;
for(int i=1;i<=n;i++){
tt=0;
stk[++tt]=0;
for(int j=1;j<=m;j++){
cin>>a[i][j];
if(a[i][j]=='F') h[j]++;
else h[j]=0;
while(tt&&h[stk[tt]]>=h[j]) tt--;
l[j]=stk[tt];
stk[++tt]=j;
}
tt=0;
stk[++tt]=m+1;
for(int j=m;j>=1;j--){
while(tt&&h[stk[tt]]>=h[j]) tt--;
r[j]=stk[tt];
stk[++tt]=j;
}
for(int j=1;j<=m;j++) ans=max(ans,h[j]*(r[j]-l[j]-1));
}
cout<<ans*3;
return 0;
}
子矩阵计数
P1950 长方形
此题从最大子矩阵问题变为子矩阵计数,其本质其实都是一样的,所以此题也能用单调栈求解。
思路跟上一题差不多,只是有一些小细节需要变化一下:
- 从求最大子矩阵面积改为子矩阵计数:
(j-l_j)\times h_j\times(r_j-j) ; -
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m,h[N],stk[N],tt,l[N],r[N];
char s[N][N];
long long ans;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>s[i]+1;
for(int i=1;i<=n;i++){
tt=0;
for(int j=1;j<=m;j++){
if(s[i][j]=='.') h[j]++;
else h[j]=0;
while(tt&&h[stk[tt]]>h[j]) tt--;
if(tt) l[j]=stk[tt];
else l[j]=0;
stk[++tt]=j;
}
tt=0;
for(int j=m;j>=1;j--){
while(tt&&h[stk[tt]]>=h[j]) tt--;
if(tt) r[j]=stk[tt];
else r[j]=m+1;
stk[++tt]=j;
}
for(int j=1;j<=m;j++) ans+=1ll*(j-l[j])*h[j]*(r[j]-j);
}
cout<<ans;
return 0;
}
极大化
当矩阵较大时,可以考虑只扫描障碍点,时间复杂度为
例题
P1578 奶牛浴场
考虑扫描障碍点。
显然,一个极大子矩阵的边界一定在障碍点上或与整个矩阵的边界重合。
考虑按横坐标从小到大枚举障碍点。为了方便,可以把整个矩阵四个角加入障碍点。先固定左边界,再扫描右边界。上下边界最初定为整个矩阵的上下边界,每次扫描更新上下边界,具体地,若当前点高于最左边的点则更新上边界,反之更新下边界。每次扫描更新答案。
又考虑到上述做法存在遗漏,即左右边界与整个矩阵左右边界存在重合又不包含那四个角的子矩阵,所以我们可以按纵坐标从小到大枚举障碍点,做法与上述做法类似。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5e3+10;
int n,m;
struct Node{
int x,y;
}a[N];
bool cmp1(Node a,Node b){
return a.x<b.x;
}
bool cmp2(Node a,Node b){
return a.y<b.y;
}
int main(){
cin>>n>>m;
int k;
cin>>k;
for(int i=1;i<=k;i++) cin>>a[i].x>>a[i].y;
int dx[4]={0,n,n,0},dy[4]={m,m,0,0};
for(int i=k+1;i<=k+4;i++) a[i]={dx[i-k-1],dy[i-k-1]};
k+=4;
sort(a+1,a+1+k,cmp1);
int ans=0;
for(int i=1;i<=k;i++){
int up=m,down=0;
for(int j=i+1;j<=k;j++){
ans=max(ans,(up-down)*(a[j].x-a[i].x));
if(a[j].y>a[i].y) up=min(up,a[j].y);
else down=max(down,a[j].y);
}
}
sort(a+1,a+1+k,cmp2);
for(int i=1;i<=k;i++){
int l=0,r=n;
for(int j=i+1;j<=k;j++){
ans=max(ans,(r-l)*(a[j].y-a[i].y));
if(a[j].x>a[i].x) r=min(r,a[j].x);
else l=max(l,a[j].x);
}
}
cout<<ans;
return 0;
}