例1:玉蟾宫
一句话题意:给出一个元素有R和F两种值的矩阵,求全为F的面积最大的子矩阵的面积。
关于这种求极大子矩阵的问题,比较常用的(本蒟蒻会的)有两种:
(1)悬线法
/*以下摘自luogu某dalao的解说(略有改动)
用途:
解决给定矩阵中满足条件的最大子矩阵
做法:
用一条线(横竖貌似都行)左右移动直到不满足约束条件或者到达边界
定义几个东西:
left[i][j]:代表从(i,j)能到达的最左位置
right[i][j]:代表从(i,j)能到达的最右位置
up[i][j]:代表从(i,j)向上扩展最长长度.
递推公式:
left[i][j]=max(left[i][j],left[i-1][j])
right[i][j]=min(right[i][j],right[i-1][j])
up[i][j]=up[i-1][j]+1
当前矩阵的面积S=长*宽(高)=(r[i][j]-l[i][j]+1)*(up[i][j])
答案
即所有S中的最大值
至于为什么递推公式中考虑上一层的情况?
是因为up数组的定义,up数组代表向上扩展最长长度, 所以需要考虑上一层的情况.
摘抄结束qwq*/
玉蟾宫code(1)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int N=1005;
inline int read(){
int X=0,w=0;char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) X=(X<<1)+(X<<3)+(ch^48),ch=getchar();
return w?-X:X;
}
inline int readc(){
char c=getchar();
while(c!='R'&&c!='F')c=getchar();
if(c=='F')return 1;
return 0;
}
int n,m,ans1,ans,map[N][N];
int l[N][N],r[N][N],up[N][N];
int main(){
register int i,j;
n=read(),m=read();
for(i=1;i<=n;i++)
for(j=1;j<=m;j++){
map[i][j]=readc();
if(map[i][j])up[i][j]=1,r[i][j]=l[i][j]=j;
}
for(i=1;i<=n;i++)
for(j=2;j<=m;j++)
if(map[i][j]==1&&map[i][j-1]==1)
l[i][j]=l[i][j-1];
for(i=1;i<=n;i++)
for(j=m-1;j>0;j--)
if(map[i][j]==1&&map[i][j+1]==1)
r[i][j]=r[i][j+1];
for(i=1;i<=n;i++)
for(j=1;j<=m;j++){
if(i>1&&map[i][j]==1&&map[i-1][j]==1){
r[i][j]=min(r[i][j],r[i-1][j]);
l[i][j]=max(l[i][j],l[i-1][j]);
up[i][j]=up[i-1][j]+1;
}
ans=max(ans,(r[i][j]-l[i][j]+1)*up[i][j]);
}
cout<<ans*3<<endl;
return 0;
}
代码应该比较好懂就没注释了。。。
(2)单调栈
我先假定你会单调栈(如果不会的推荐看这里),我们知道利用单调栈,可以找到数列中任意元素中从左/右遍历第一个比它小/大的元素的位置,利用这一性质能解决的比较经典的问题有求直方图中最大矩形的面积。
那么这道题其实也可以转化成求直方图中最大矩形的面积,具体做法是:
- 预处理s[i][j]表示(i,j)向上有几个连续的"F"
- 用单调栈统计两个数组:l[i][j]表示s[i][j]的左边第一个比s[i][j]小的位置,r[i][j]表示s[i][j]的右边第一个比s[i][j]小的位置
- 那么此时矩形的面积就是(r[i][j]-1-l[i][j])*s[i][j]
- 返回到2.,如此对每一行处理
对于第一步,可以想象处理出了许多条竖直的线段。处理到第i行就以第i行为下界与竖直的线段构成了一个直方图,然后第2、3步就直接套用求直方图中最大矩形的面积的做法就行了,s就相当于高度,l和r分别是左右两边能控制的极位置。
玉蟾宫code(2)
#include<cstdio> #include<cstring> #include<stack> #include<algorithm> #include<iostream> #define lop(i,s,t) for(register int (i)=(s);(i)<=(t);++(i))//我懒到写宏定义了 。。。 #define nop(i,s,t) for(register int (i)=(s);(i)>=(t);--(i))//应该不算太丑吧。。。 using namespace std; const int N=1005; inline bool getc(){ char ch=0; while(ch^'R'&&ch^'F') ch=getchar(); return ch=='F'; } bool mp[N][N]; int n,m,ans,s[N][N],l[N][N],r[N][N]; stack<int> st; inline void init(){ lop(i,1,n) lop(j,1,m) if(mp[i][j]=getc()) //如果(i,j)合法,那么它可以向上延伸(或者说可以接在上面的竖线之下) s[i][j]=s[i-1][j]+1; } inline void clear(){while(!st.empty()) st.pop();} //没办法,stack没有clear() inline void solve(){ lop(i,1,n){ clear(); //不要忘了清空栈 lop(j,1,m){ while(!st.empty()&&s[i][j]<=s[i][st.top()]) st.pop(); if(st.empty()) l[i][j]=0; else l[i][j]=st.top(); st.push(j); } clear(); nop(j,m,1){ while(!st.empty()&&s[i][j]<=s[i][st.top()]) st.pop(); if(st.empty()) r[i][j]=m+1; //边的边界定为m+1 else r[i][j]=st.top(); st.push(j); } } lop(i,1,n) lop(j,1,m) if(s[i][j]) //有高度的才能统计答案,其实也可以不加,反正没有高度的话乘出来是0不影响答案 ans=max(ans,(r[i][j]-1-l[i][j])*s[i][j]); } int main() { cin>>n>>m; init(); solve(); cout<<ans*3<<endl; return 0; }