所需知识:单调栈
首先想到的就是暴力做法,先用二维前缀和将每个矩形的和预处理出来,然后再依次枚举每个矩形的上下左右边界,判断每个矩形内的和是否为0(如果土地没被破坏则为0,被破坏赋成1),将所有矩形遍历完即可得到最大的矩形
二维前缀和:
例:
j/i | 1 | 2 | 3 | 4 | 5 | 6 |
1 | 2 | 2 | 4 | 5 | 1 | 3 |
2 | 3 | 3 | 6 | 5 | 3 | 1 |
3 | 6 | 5 | 4 | 8 | 3 | 2 |
4 | 5 | 7 | 5 | 3 | 5 | 4 |
5 | 1 | 3 | 3 | 2 | 2 | 3 |
6 | 3 | 1 | 2 | 4 | 3 | 6 |
sum[i][j](黑色矩形)=sum[i-1][j](蓝色矩形)+sum[i][j-1](红色矩形)-sum[i-1][j-1](橙色矩形);
尽管我们有思路也可以写出代码,但是枚举上下左右每个边界的时间复杂度为O(n4),由于n为3000,n4大概有8e13,显然数据范围过大过不了,所以需要优化,不能枚举上下左右四个边界,
我们可以枚举下边界,然后将每一次看作求直方图中的最大矩形,如果没学过如何求直方图中的最大矩形,可以参考下这篇blog
之后只需要从上到下枚举下边界就可以求出最大矩形了。
C++代码:
#include<iostream>
#include<stack>
#include<algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int N=3005;
int R,C,P;
int Map[N][N];
int h[N][N];
int l[N],r[N];
int st[N];//用数组模拟stack
int main(){
cin>>R>>C>>P;
for(int i=1;i<=P;i++){
int r,c;
cin>>r>>c;
Map[r][c]=1;
}
for(int i=1;i<=C;i++){//将破坏处变成1
for(int j=1;j<=R;j++){
if(Map[j][i])
h[j][i]=0;
else
h[j][i]=h[j-1][i]+1;
}
}
ll res=0;
for(int i=1;i<=R;i++){//从上到下枚举下边界
h[i][0]=h[i][C+1]=-1;//将两个边界初始化为-1可以不用再特判边界
int tt=0;
st[++tt]=0;
for(int j=1;j<=C;j++){
while(h[i][j]<=h[i][st[tt]]) tt--;
l[j]=st[tt];
st[++tt]=j;
}
tt=0;
st[++tt]=C+1;
for(int j=C;j>0;j--){
while(h[i][j]<=h[i][st[tt]]) tt--;
r[j]=st[tt];
st[++tt]=j;
}
//取每次的矩形与之前最大的矩形之间的最大值
for(int j=1;j<=C;j++){
res=max(res,1LL*(r[j]-l[j]-1)*h[i][j]);
}
}
cout<<res;
return 0;
}