LA3029:题意给你一个矩阵,每个单元格要么为空,要么为满,求全是空的最大子矩阵的格子数*3.
最朴素的算法是枚举所有的子矩阵的左上角的点和右下角的点,这样就能确定一个矩形,然后再遍历这个矩阵,看矩阵是否全是空。复杂度大概是O(m^2*n^2)
通过扫描法,每次维护left[i][j],right[i][j],up[i][j],可以简化到O(mn)
曾经有想过只维护left,像前缀那样,不过后来发现如果这样的话,不能保证每次这个单元格上面全是空,所以还得维护right。
left[i][j]表示某个点的向左运动极限,right[i][j]表示某个点向右运动极限,都用列的编号来表示。up[i][j]表示向上的运动长度,用数字的表示。
这样子矩阵就能表示为(right[i][j]-left[i][j]+1)*up[i][j],求最大值即可.
转移方程:
第一行:
由于不受上面一行的限制:
如果为满:left[i][j]=0,right[i][j]=n,up[i][j]=0,这里是为了下一行准备。可以模拟试一试。
如果为空:left[i][j]=lo+1,right[i][j]=ro-1,up[i][j]=1;
不是第一行:
如果为满:left[i][j]=0,right[i][j]=n,up[i][j]=0;
如果为空:left[i][j]=max(lo+1,left[i-1][j]),right[i][j]=min(ro-1,right[i-1][j]),up[i][j]=up[i-1][j];
代码:
#include<iostream> #include<cstdio> #include<vector> #include<string> #include<queue> #include<cmath> #include<algorithm> #include<cstring> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define maxn 1005 #define INF 0xfffffff #define mem(a,b) memset(a,b,sizeof(a)) #define FOR(i,s,t) for(int i=s;i<=t;i++) #define ull unsigned long long #define ll long long using namespace std; int map[maxn][maxn],Left[maxn][maxn],Right[maxn][maxn],up[maxn][maxn]; int main() { int t; scanf("%d",&t); while(t--) { int n,m; scanf("%d%d",&n,&m); for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { char ch=getchar(); while(ch!='R'&&ch!='F') ch=getchar(); if(ch=='R') map[i][j]=1; else map[i][j]=0; } } //维护Left Right up lo ro int ans=0,lo,ro; for(int i=0;i<n;i++) { lo=-1,ro=m; for(int j=0;j<m;j++) { if(map[i][j]==1){lo=j,up[i][j]=0,Left[i][j]=0;}//Left[i][j]=0,因为考虑到对下一行的影响 else { if(i==0) { up[i][j]=1; Left[i][j]=lo+1; } else { up[i][j]=up[i-1][j]+1; Left[i][j]=max(lo+1,Left[i-1][j]); } } } //维护Right for(int j=m-1;j>=0;j--) { if(map[i][j]==1){ro=j,Right[i][j]=n;} else { if(i==0) { Right[i][j]=ro-1; } else Right[i][j]=min(ro-1,Right[i-1][j]); } ans=max(ans,(Right[i][j]-Left[i][j]+1)*up[i][j]); } } printf("%d\n",ans*3); } return 0; }