原题: https://www.luogu.org/problemnew/show/P2216
题意:
给出n*m的数字矩阵,框出一个c*c正方形使最大值-最小值最小。
解析:
这题昨晚睡前想了一下,只要能快速处理一个区间的最值就可以暴力了,管他怎么dp。线段树有感觉大材小用了感觉。
想到用优先队列可以快速得出1*c那么一条的最值,又想到了一个正方形是c条1*c组成的,那么问题就解决啦。
结果想完这题后就睡不着了,作孽啊~
早上起来敲了一发优先队列TLE了
#include
using namespace std;
struct node{
int v,idx;
node(){}
node(int v,int idx):v(v),idx(idx){}
bool operator <(const node &r)const{
return v<r.v;
}
bool operator >(const node &r)const{
return v>r.v;
}
};
priority_queue<node>Qmax;
priority_queue<node,vector<node>,greater<node> >Qmin;
int a[1009][1009];
int minn[1009][1009];
int maxx[1009][1009];
int main(){
int n,m,c;scanf("%d%d%d",&n,&m,&c);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",a[i]+j);
}
}
for(int j=1;j<=m;j++){
while(!Qmin.empty())Qmin.pop();
while(!Qmax.empty())Qmax.pop();
for(int i=1;i<c;i++)Qmin.push(node(a[i][j],i)),Qmax.push(node(a[i][j],i));
for(int i=c;i<=n;i++){
Qmin.push(node(a[i][j],i)),Qmax.push(node(a[i][j],i));
while(Qmin.top().idx<i-c+1)Qmin.pop();
while(Qmax.top().idx<i-c+1)Qmax.pop();
minn[i-c+1][j]=Qmin.top().v;
maxx[i-c+1][j]=Qmax.top().v;
}
}
int ans=0x3f3f3f3f;
for(int i=1;i<=n-c+1;i++){
while(!Qmin.empty())Qmin.pop();
while(!Qmax.empty())Qmax.pop();
for(int j=1;j<c;j++)Qmin.push(node(minn[i][j],j)),Qmax.push(node(maxx[i][j],j));
for(int j=c;j<=m;j++){
Qmin.push(node(minn[i][j],j)),Qmax.push(node(maxx[i][j],j));
while(Qmin.top().idx<j-c+1)Qmin.pop();
while(Qmax.top().idx<j-c+1)Qmax.pop();
ans=min(ans,Qmax.top().v-Qmin.top().v);
}
}
printf("%d\n",ans);
}
再想了一下,其实单调队列就可以维护,和单调栈一样的做法,只是多了一个bottom,最大值存在Q[bottom]罢了(bottom初值为1)。
#include
using namespace std;
#define DEBU
#define debug(i) printf("# %d\n",i);
int a[1009][1009];
int minn[1009][1009];
int maxx[1009][1009];
int qmin[1009],qmax[1009],top1,bottom1,top2,bottom2;
int main(){
int n,m,c;scanf("%d%d%d",&n,&m,&c);
//int n=1000,m=1000,c=100;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",a[i]+j);
//a[i][j]=rand();
}
}
for(int j=1;j<=m;j++){
top1=top2=0;
bottom1=bottom2=1;
for(int i=1;i<c;i++){
while(top1>=bottom1&&a[i][j]<a[qmin[top1]][j])top1--;
qmin[++top1]=i;
while(top2>=bottom2&&a[i][j]>a[qmax[top2]][j])top2--;
qmax[++top2]=i;
}
for(int i=c;i<=n;i++){
while(top1>=bottom1&&a[i][j]<a[qmin[top1]][j])top1--;
qmin[++top1]=i;
while(top2>=bottom2&&a[i][j]>a[qmax[top2]][j])top2--;
qmax[++top2]=i;
while(qmin[bottom1]<i-c+1)bottom1++;
while(qmax[bottom2]<i-c+1)bottom2++;
minn[i-c+1][j]=a[qmin[bottom1]][j];
maxx[i-c+1][j]=a[qmax[bottom2]][j];
}
}
#ifdef DEBUG
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
printf("(%d %d) ",minn[i][j],maxx[i][j]);
}
printf("\n");
}
#endif // DEBUG
int ans=0x3f3f3f3f;
for(int i=1;i<=n-c+1;i++){
top1=top2=0;
bottom1=bottom2=1;
for(int j=1;j<c;j++){
while(top1>=bottom1&&minn[i][j]<minn[i][qmin[top1]])top1--;
qmin[++top1]=j;
while(top2>=bottom2&&maxx[i][j]>maxx[i][qmax[top2]])top2--;
qmax[++top2]=j;
}
for(int j=c;j<=m;j++){
while(top1>=bottom1&&minn[i][j]<minn[i][qmin[top1]])top1--;
qmin[++top1]=j;
while(top2>=bottom2&&maxx[i][j]>maxx[i][qmax[top2]])top2--;
qmax[++top2]=j;
while(qmin[bottom1]<j-c+1)bottom1++;
while(qmax[bottom2]<j-c+1)bottom2++;
ans=min(ans,maxx[i][qmax[bottom2]]-minn[i][qmin[bottom1]]);
}
}
printf("%d\n",ans);
}
去看了别人的题解,ST表也可以做,这个原理是一样的,用ST表处理一条的最值,再用ST表处理多条之间的最值。
所以说这是道水题了。。。