https://ac.nowcoder.com/acm/contest/883/F
题意:给定一个
n
×
n
n×n
n×n的矩阵,求面积最大的子矩阵,使得该子矩阵中任意两点
(
i
,
j
)
,
(
k
,
l
)
(i,j),(k,l)
(i,j),(k,l)的权值
∣
a
i
,
j
−
a
k
,
l
∣
≤
m
|a_{i,j}-a_{k,l}|≤m
∣ai,j−ak,l∣≤m。
1
≤
n
≤
500
,
0
≤
m
≤
1
0
5
,
1
≤
a
i
,
j
≤
1
0
5
1≤n≤500,0≤m≤10^5,1≤a_{i,j}≤10^5
1≤n≤500,0≤m≤105,1≤ai,j≤105。
首先枚举上下边界,并维护每一列的最大值和最小值。
然后枚举右边界,同时维护最大值和最小值两个单调队列,最大值的单调队列是递减的,最小值的单调队列是递增的,这样我们就可以利用这两个单调队列得到符合条件的最小的左边界,从而在
O
(
n
3
)
O(n^3)
O(n3)时间复杂度内实现。
要注意的是STL的deque很慢,要手写单调队列,不然会T。
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <list>
using namespace std;
struct node{
int id,v;
node(int _id=0,int _v=0)
{
id=_id;v=_v;
}
};
int n,m,T,ans,a[550][550],mi[550],ma[550];
node miq[550],maq[550];
int main()
{
scanf("%d",&T);
while(T--)
{
ans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
mi[j]=a[i][j];
ma[j]=a[i][j];
}
for(int j=i;j<=n;j++)
{
for(int k=1;k<=n;k++)
{
mi[k]=min(a[j][k],mi[k]);
ma[k]=max(a[j][k],ma[k]);
}
int mif=1,mib=0,maf=1,mab=0;
int l=1;
for(int k=1;k<=n;k++)
{
while(mif<=mib&&miq[mib].v>mi[k])
mib--;
miq[++mib]=node(k,mi[k]);
while(maf<=mab&&maq[mab].v<ma[k])
mab--;
maq[++mab]=node(k,ma[k]);
while(mif<=mib&&maf<=mab&&maq[maf].v-miq[mif].v>m)
{
if(maq[maf].id<miq[mif].id)
{
l=maq[maf].id+1;
maf++;
}
else if(maq[maf].id>miq[mif].id)
{
l=miq[mif].id+1;
mif++;
}
else if(maq[maf].id==miq[mif].id)
{
l=maq[maf].id+1;
maf++;mif++;
}
}
ans=max(ans,(k-l+1)*(j-i+1));
}
}
}
printf("%d\n",ans);
}
return 0;
}