题目链接:https://ac.nowcoder.com/acm/contest/883/F
思路:枚举上下边界,以及右边界即可。那么在我们知道上下边界时,怎样快速地计算最大的矩阵呢,注意到我们枚举右边界时是 从左到右的,左边的数我们知道了,就要想办法充分利用这些信息,假设对于每一列的最大值以及最小值我们都是知道的,那么用两个单调队列分别维护最小值最大值,最小值递增,最大值递减。那么在判断区间是否合法时只需取出队首最大值以及最小值即可判断。那么怎样快速的知道对于固定的上下边界的每一列的最值呢。同样的想法,下边界每次增加1时,只是增加了一行的信息,我们就可以在这个时候更新最值信息了。
OK 接下来就是写代码了,自己写的队列因为两个队列的首元素的位置不相等,wa了很多次,后来看了别人的代码,把位置改相同,过了。
#include<iostream>
using namespace std;
const int N = 510;
int a[N][N];
int n,m;
int mx[N],mi[N];
void init(){
for(int i=1;i<=n;i++)mx[i]=-1e6,mi[i]=1e6;
}
struct{
int s,t;
int id[N];
void init(){
t=0;s=1;
}
void PUSH(int x,int type){
if(type==1){
while(s<=t&&mx[x]>mx[id[t]])t--;
id[++t]=x;
}
else{
while(s<=t&&mi[x]<mi[id[t]])t--;
id[++t]=x;
}
}
void POP(){
s++;
}
int BACK(int type){
if(type)return mx[id[s]];
else return mi[id[s]];
}
int ID(){
return id[s];
}
bool Empty(){return s<=t;}
void pri(){
for(int i=s;i<=t;i++)cout<<mx[id[i]]<<"ll ";
cout<<endl;
}
}MAX,MIN;
int main(){
int t ; scanf("%d",&t);
while(t--){
int 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 up=1;up<=n;up++){
init();
for(int down=up;down<=n;down++){
for(int i=1;i<=n;i++){
mi[i]=min(a[down][i],mi[i]);
mx[i]=max(mx[i],a[down][i]);
}
MAX.init();
MIN.init();
int R=0;
int L=1;
while(R<=n){
if(R-L+1==0||MAX.BACK(1)-MIN.BACK(0)<=m){
ans=max(ans,(down-up+1)*(R-L+1));
R++;
MAX.PUSH(R,1);
MIN.PUSH(R,0);
}
else{
if(MAX.ID()==L)MAX.POP();
if(MIN.ID()==L)MIN.POP();
L++;
}
}
}
}
printf("%d\n",ans);
}
return 0;
}
错误代码:
#include<iostream>
using namespace std;
const int N = 510;
int a[N][N];
int n,m;
int mx[N],mi[N];
void init(){
for(int i=1;i<=n;i++)mx[i]=-1e6,mi[i]=1e6;
}
struct{
int s,t;
int id[N];
void init(){
t=0;s=1;
}
void PUSH(int x,int type){
if(type==1){
while(s<=t&&mx[x]>mx[id[t]])t--;
id[++t]=x;
}
else{
while(s<=t&&mi[x]<mi[id[t]])t--;
id[++t]=x;
}
}
void POP(){
s++;
}
int BACK(int type){
if(type)return mx[id[s]];
else return mi[id[s]];
}
int ID(){
return id[s];
}
bool Empty(){return s<=t;}
void pri(){
for(int i=s;i<=t;i++)cout<<mx[id[i]]<<"ll ";
cout<<endl;
}
}MAX,MIN;
int main(){
int t ; scanf("%d",&t);
while(t--){
int 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 up=1;up<=n;up++){
init();
for(int down=up;down<=n;down++){
for(int i=1;i<=n;i++){
mi[i]=min(a[down][i],mi[i]);
mx[i]=max(mx[i],a[down][i]);
}
MAX.init();
MIN.init();
for(int R=1;R<=n;R++){
MAX.PUSH(R,1);
MIN.PUSH(R,0);
// MAX.pri();
// cout<<"____________\n";
while(MAX.Empty()&&MIN.Empty()&&MAX.BACK(1)-MIN.BACK(0)>m){
if(MAX.ID()>MIN.ID())MIN.POP();
else if(MAX.ID()<MIN.ID())MAX.POP();
else{
MAX.POP();
MIN.POP();
}
}
if(MAX.Empty()&&MIN.Empty()){
ans=max(ans,(down-up+1)*(R+1-max(MAX.ID(),MIN.ID())));
// cout<<up<<" "<<down<<" "<<max(MAX.ID(),MIN.ID())<<" "<<R<<"pl \n";
}
}
}
}
printf("%d\n",ans);
}
return 0;
}