BZOJ上面是总时间,所以这一道题在BZOJ上是可以直接用滚动数组水过去的,但是还是不推荐,不过还是写出来吧
#include<cstdio>
#include<cstring>
#include<iostream>
#define mmax(a,b,c,d) max(a,max(b,max(c,d)))
#define mmin(a,b,c,d) min(a,min(b,min(c,d)))
using namespace std;
int f[1024][1024],m,n,K,g[1024][1024],mat[1024][1024];
int read(){
int x=0;
char c=getchar();int flag=1;
for(;c>'9'||c<'0';c=getchar()){if(c=='-')flag=-1;}
for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';
x=x*flag;
return x;
}
int main(){
scanf("%d%d%d",&n,&m,&K);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
mat[i][j]=read();
f[i][j]=g[i][j]=mat[i][j];
}
}
for(int k=2;k<=K;k++){
for(int i=1;i+k-1<=n;i++){
for(int j=1;j+k-1<=m;j++){
f[i][j]=mmax(mat[i][j],f[i+1][j],f[i][j+1],f[i+1][j+1]);
g[i][j]=mmin(mat[i][j],g[i+1][j],g[i][j+1],g[i+1][j+1]);
}
}
}
int ans=1e9;
for(int i=1;i+K-1<=n;i++){
for(int j=1;j+K-1<=m;j++){
ans=min(ans,f[i][j]-g[i][j]);
}
}
printf("%d",ans);
return 0;
}
好了,不要只是水题就满足了,正解应该是跑两次单调队列,一次计算一行连续k的最小最大,然后再利用跑出来的一列中连续k个元素的极值来跑行就可以计算出这一个矩阵了
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 1020
using namespace std;
int n,m,k,mat[maxn][maxn],f[3][maxn][maxn];
pair<int,int> q[maxn*3];
void front(int pos){//0是最小值1是最大值
for(int i=1;i<=n;i++){
int l=1,r=0;
for(int j=1;j<=m;j++){
while(l<=r&&j-q[l].first+1>k)l++;
if(pos==0)while(l<=r&&q[r].second>=mat[i][j])r--;
else while(l<=r&&q[r].second<=mat[i][j])r--;
q[++r]=make_pair(j,mat[i][j]);
f[pos][i][j]=q[l].second;
}
}
for(int i=k;i<=m;i++){
int l=1,r=0;
for(int j=1;j<=n;j++){
while(l<=r&&j-q[l].first+1>k)l++;
if(pos==0)while(l<=r&&q[r].second>=f[pos][j][i])r--;
else while(l<=r&&q[r].second<=f[pos][j][i])r--;
q[++r]=make_pair(j,f[pos][j][i]);
f[pos][j][i]=q[l].second;
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)scanf("%d",&mat[i][j]);
front(0),front(1);
int ans=1e9;
for(int i=k;i<=n;i++)
for(int j=k;j<=m;j++)ans=min(ans,f[1][i][j]-f[0][i][j]);
printf("%d",ans);
return 0;
}