题目大意: 在一个 n × m n\times m n×m 的大矩形里面找一个 a × b a\times b a×b 的子矩形,再从子矩形里面找一个 c × d c\times d c×d 的子子矩形,要求子子矩形的边沿不能和子矩形的边沿重合(即子矩形完全包裹子子矩形),在此条件下,找到 子矩形之和 减 子子矩形之和 的最大值。
题解
显然我们可以预处理一个二维前缀和,这样就可以 O ( 1 ) O(1) O(1) 求出某个矩形的和。
子矩形我们可以 O ( n × m ) O(n\times m) O(n×m) 枚举,对于每个子矩形,我们只需要找到里面最小的子子矩形即可。由于子子矩形的大小固定,我们可以先横向跑一遍单调队列,再纵向跑一次单调队列,就可以求出每个子矩形里面的最小子子矩形了。
代码如下:
#include <cstdio>
#define maxn 1010
int n,m,a,b,c,d,sum[maxn][maxn];
int su[maxn][maxn],mi[maxn][maxn];
int getsum(int x,int y,int xx,int yy){return sum[xx][yy]-sum[xx][y-1]-sum[x-1][yy]+sum[x-1][y-1];}
struct node{int x,y;}q[maxn];
int st,ed,ans=0;
int max(int x,int y){return x>y?x:y;}
int main()
{
scanf("%d %d %d %d %d %d",&n,&m,&a,&b,&c,&d);
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&sum[i][j]),sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
for(int i=c;i<=n;i++)for(int j=d;j<=m;j++)su[i][j]=getsum(i-c+1,j-d+1,i,j);
for(int i=c+1;i<=n;i++)
{
st=1;ed=0;
for(int j=1;j<=m;j++)
{
while(st<=ed&&q[st].y<=j-b+d)st++; while(st<=ed&&q[ed].x>su[i][j])ed--; q[++ed]=(node){su[i][j],j};
if(j>=b-1)mi[i][j]=q[st].x;
}
}
for(int j=b;j<=m;j++)
{
st=1;ed=0;
for(int i=2;i<=n;i++)
{
while(st<=ed&&q[st].y<=i-a+c)st++; while(st<=ed&&q[ed].x>mi[i-1][j-1])ed--; q[++ed]=(node){mi[i-1][j-1],i-1};
if(i>=a)ans=max(ans,getsum(i-a+1,j-b+1,i,j)-q[st].x);
}
}
printf("%d",ans);
}