Vjudge传送门
题意:
这道题是要我们找出一个面积最大的子矩阵。然后让我们输出这个矩阵的面积乘3的结果(其实我不是特别明白为什么要乘三,乘三难道会让这道题的难度陡增???),然后矩阵描述是R和F,F表示是空地,R表示是障碍,只有是空地的地方才可以被划为子矩阵…
举个例子
F F F
F F R
R F F
的答案是2
题解:
显然我们这道题如果直接暴力的话,会非常难受,虽然我们可以维护01矩阵的二维前缀和,但即便是这样,也是非常难受的因为,n和m的范围是1000,而你维护了二维前缀和之后的时间至少是
n4
吧
233
。
所以我们的做法其实很简单,维护一个
up[i][j]
数组,表示这个位置往下可以放多少个位置,也就是说往下可以数多少个空地,这个显然可以写出转移方程
up[i][j]=up[i−1][j]+1
再维护一个 left[i][j] 数组(貌似只能维护 Left 数组,因为 left 好像冲突)
表示我们可以从这个点最多追溯到 左边的某个点
再维护一个 right[i][j] 数组( right 好像也冲突),所以我们用这个表示 i,j 这个位置最多可以追溯到右边的空地…举个例子
0123
1FFF
2FFR
3EFF
…垃圾latex没法对齐(显然是我不懂latex语法)
比如这个 Left[3][3]=2,RIght[3][2]=3
我们稍加思考(当然 ZBW 大佬可以不加思考,%%%)
可以发现如下转移方程
Left[i][j]=max(Left[i−1][j],lo+1)
Right[i][j]=min(Right[i−1][j],ro−1)
其中 lo 表示现在扫这一行,扫到的最左边一个非空地的位置
同理 ro 表示现在扫的这一行,扫到的最右边一个非空地的位置
于是我们就可以开始愉快地进行递(动态)推(规划)啦
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=1000;
int mat[MAXN][MAXN],up[MAXN][MAXN],Left[MAXN][MAXN],Right[MAXN][MAXN];
int main(){int T;
scanf("%d",&T);
while(T--){
int n,m;
scanf("%d%d",&m,&n);
for(register int i=0;i<m;i++){
for(register int j=0;j<n;j++){
int ch=getchar();while(ch!='F'&&ch!='R') ch=getchar();
mat[i][j]=ch=='F'?0:1;
}
}
int ans=0;
for(register int i=0;i<m;i++){
int lo=-1,ro=n;
for(register int j=0;j<n;j++){
if(mat[i][j]==1){
Left[i][j]=up[i][j]=0;lo=j;
}else{
up[i][j]=i==0?1:up[i-1][j]+1;
Left[i][j]=i==0?lo+1:max(Left[i-1][j],lo+1);
}
}
for(register int j=n-1;j>=0;j--){
if(mat[i][j]==1){
Right[i][j]=n;ro=j;
}else{
Right[i][j]=(i==0)?ro-1:min(Right[i-1][j],ro-1);
ans=max(ans,up[i][j]*(Right[i][j]-Left[i][j]+1));
}
}
}
printf("%d\n",ans*3);
}
return 0;
}