bzoj 1047 //1047: [HAOI2007]理想的正方形 朴素算法/动归/动归 滚动数组优化/二维RMQ/二维RMQ 滚动数组优化/单调队列分别维护行与列
bzoj 1047 //1047: [HAOI2007]理想的正方形 //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1047
更多题解,详见https://blog.csdn.net/mrcrack/article/details/90228694BZOJ刷题记录
方法一:朴素算法
20分 洛谷https://www.luogu.org/problem/P2216提交
#include
#define maxn 1010
int a,b,n,mp[maxn][maxn],ans=2000000300;
int max(int a,int b){
return a>b?a:b;
}
int min(int a,int b){
return a }
int main(){
int i,j,p,q,mx,mn;
scanf("%d%d%d",&a,&b,&n);
for(i=1;i<=a;i++)
for(j=1;j<=b;j++)
scanf("%d",&mp[i][j]);
for(i=1;i+n-1<=a;i++)//此处错写成for(i=1;i-n+1<=a;i++)
for(j=1;j+n-1<=b;j++){//此处错写成for(j=1;j-n+1<=b;j++){
mx=-1000000100,mn=1000000100;
for(p=0;p<=n-1;p++)
for(q=0;q<=n-1;q++){
mx=max(mx,mp[i+p][j+q]);
mn=min(mn,mp[i+p][j+q]);
}
if(ans>mx-mn)ans=mx-mn;
}
printf("%d\n",ans);
return 0;
}
方法二:动归
40分 洛谷https://www.luogu.org/problem/P2216提交
//以下内容摘自https://www.luogu.org/problemnew/solution/P2216 作者: Aisaka1436 更新时间: 2016-10-20 18:54
/*
用maxv(i,j,k)表示以点(i,j)为左上角的边长为k的矩形中的最大值,然后用递推公式
maxv(i,j,k)=max{maxv(i,j,k-1), maxv(i+1,j+1,k-1), maxv(i+1,j,k-1), maxv(i,j+1,k-1)}
*/
//竟然遇到一个 已杀死 问题,猜测数组开得过大mx[maxn][maxn][105],mn[maxn][maxn][105]
//int a,b,n,mp[maxn][maxn],ans=2000000300,mx[maxn][maxn][105],mn[maxn][maxn][105];
//1000*1000*100*4/1024/1024=381.5MB
//很无奈,mx[maxn][maxn][105]爆空间,只能改成mx[maxn][maxn][21],硬着头皮提交。2019-11-4
#include
#define maxn 1010
int a,b,n,mp[maxn][maxn],ans=2000000300,mx[maxn][maxn][21],mn[maxn][maxn][21];//int a,b,n,mp[maxn][maxn],ans=2000000300,mx[maxn][maxn][105],mn[maxn][maxn][105];
int max(int a,int b){
return a>b?a:b;
}
int min(int a,int b){
return a }
int main(){
int i,j,k;
scanf("%d%d%d",&a,&b,&n);
for(i=1;i<=a;i++)
for(j=1;j<=b;j++)
scanf("%d",&mp[i][j]),mx[i][j][1]=mn[i][j][1]=mp[i][j];
for(k=2;k<=n;k++)
for(i=1;i+k-1<=a;i++)
for(j=1;j+k-1<=b;j++){
int mmx=-1000000100,mmn=1000000100;
mmx=max(mx[i][j][k-1],mmx),mmx=max(mx[i+1][j+1][k-1],mmx);
mmx=max(mx[i+1][j][k-1],mmx),mmx=max(mx[i][j+1][k-1],mmx);
mmn=min(mn[i][j][k-1],mmn),mmn=min(mn[i+1][j+1][k-1],mmn);
mmn=min(mn[i+1][j][k-1],mmn),mmn=min(mn[i][j+1][k-1],mmn);
mx[i][j][k]=mmx,mn[i][j][k]=mmn;
}
for(i=1;i+n-1<=a;i++)
for(j=1;j+n-1<=b;j++)
if(ans>mx[i][j][n]-mn[i][j][n])ans=mx[i][j][n]-mn[i][j][n];
printf("%d\n",ans);
return 0;
}
方法三:动归 滚动数组优化
70分 洛谷https://www.luogu.org/problem/P2216提交
//以下内容摘自https://www.luogu.org/problemnew/solution/P2216?page=6 作者: cn:苏卿念 更新时间: 2018-08-03 10:45
/*
*/
//样例通过,提交70分.2019-11-4
#include
#define maxn 1010
int a,b,n,mp[maxn][maxn],ans=2000000300,mx[maxn][maxn],mn[maxn][maxn];
int max(int a,int b){
return a>b?a:b;
}
int min(int a,int b){
return a }
int main(){
int i,j,k;
scanf("%d%d%d",&a,&b,&n);
for(i=1;i<=a;i++)
for(j=1;j<=b;j++)
scanf("%d",&mp[i][j]),mx[i][j]=mn[i][j]=mp[i][j];
for(k=2;k<=n;k++)
for(i=1;i+k-1<=a;i++)
for(j=1;j+k-1<=b;j++){
mx[i][j]=max(mx[i+1][j+1],mx[i][j]),mx[i][j]=max(mx[i+1][j],mx[i][j]),mx[i][j]=max(mx[i][j+1],mx[i][j]);
mn[i][j]=min(mn[i+1][j+1],mn[i][j]),mn[i][j]=min(mn[i+1][j],mn[i][j]),mn[i][j]=min(mn[i][j+1],mn[i][j]);
}
for(i=1;i+n-1<=a;i++)
for(j=1;j+n-1<=b;j++)
if(ans>mx[i][j]-mn[i][j])ans=mx[i][j]-mn[i][j];
printf("%d\n",ans);
return 0;
}
方法四:二维RMQ
//此文https://www.luogu.org/problemnew/solution/P2216?page=6 作者: yangzechenc 更新时间: 2018-08-09 11:41 查询代码写得很棒
/*
*/
//此文https://www.cnblogs.com/GXZlegend/p/7491533.html代码写得更棒,尤其是边界处理。
//边界处理,比较花时间。
//样例通过,提交0分,测试点2,3WA,1,4-10RE
//错误代码如下
/*
for(k=1;(1<
t1=max(mx[i][j][k-1],mx[i+1<<(k-1)][j][k-1]);
t2=max(mx[i][j+1<<(k-1)][k-1],mx[i+1<<(k-1)][j+1<<(k-1)][k-1]);
mx[i][j][k]=max(t1,t2);
t1=min(mn[i][j][k-1],mn[i+1<<(k-1)][j][k-1]);
t2=min(mn[i][j+1<<(k-1)][k-1],mn[i+1<<(k-1)][j+1<<(k-1)][k-1]);
mn[i][j][k]=min(t1,t2);
}
k--;
for(i=1;i+n-1<=a;i++)
for(j=1;j+n-1<=b;j++){
int t1,t2,mmx,mmn;
t1=max(mx[i][j][k-1],mx[i+n-1<<(k-1)][j][k-1]);//不用上面循环方法的原因是,上面算法,可能会超越所求矩阵范围
t2=max(mx[i][j+n-1<<(k-1)][k-1],mx[i+n-1<<(k-1)][j+n-1<<(k-1)][k-1]);
mmx=max(t1,t2);
t1=min(mn[i][j][k-1],mn[i+n-1<<(k-1)][j][k-1]);//不用上面循环方法的原因是,上面算法,可能会超越所求矩阵范围
t2=min(mn[i][j+n-1<<(k-1)][k-1],mn[i+n-1<<(k-1)][j+n-1<<(k-1)][k-1]);
mmn=min(t1,t2);
if(ans>mmx-mmn)ans=ans>mmx-mmn;
}
*/
//修改,提交,全WA,修改代码如下
/*
for(k=1;(1<
t1=max(mx[i][j][k-1],mx[i+(1<<(k-1))][j][k-1]);
t2=max(mx[i][j+(1<<(k-1))][k-1],mx[i+(1<<(k-1))][j+(1<<(k-1))][k-1]);
mx[i][j][k]=max(t1,t2);
t1=min(mn[i][j][k-1],mn[i+(1<<(k-1))][j][k-1]);
t2=min(mn[i][j+(1<<(k-1))][k-1],mn[i+(1<<(k-1))][j+(1<<(k-1))][k-1]);
mn[i][j][k]=min(t1,t2);
}
k--;
for(i=1;i+n-1<=a;i++)
for(j=1;j+n-1<=b;j++){
int t1,t2,mmx,mmn;
t1=max(mx[i][j][k-1],mx[i+n-(1<<(k-1))][j][k-1]);//不用上面循环方法的原因是,上面算法,可能会超越所求矩阵范围
t2=max(mx[i][j+n-(1<<(k-1))][k-1],mx[i+n-(1<<(k-1))][j+n-(1<<(k-1))][k-1]);
mmx=max(t1,t2);
t1=min(mn[i][j][k-1],mn[i+n-(1<<(k-1))][j][k-1]);//不用上面循环方法的原因是,上面算法,可能会超越所求矩阵范围
t2=min(mn[i][j+n-(1<<(k-1))][k-1],mn[i+n-(1<<(k-1))][j+n-(1<<(k-1))][k-1]);
mmn=min(t1,t2);
if(ans>mmx-mmn)ans=ans>mmx-mmn;
}
*/
//继续排查代码,发现错误,如下
/*
for(i=1;i+n-1<=a;i++)
for(j=1;j+n-1<=b;j++){
int t1,t2,mmx,mmn;
t1=max(mx[i][j][k-1],mx[i+n-(1<<(k-1))][j][k-1]);//不用上面循环方法的原因是,上面算法,可能会超越所求矩阵范围
t2=max(mx[i][j+n-(1<<(k-1))][k-1],mx[i+n-(1<<(k-1))][j+n-(1<<(k-1))][k-1]);
mmx=max(t1,t2);
t1=min(mn[i][j][k-1],mn[i+n-(1<<(k-1))][j][k-1]);//不用上面循环方法的原因是,上面算法,可能会超越所求矩阵范围
t2=min(mn[i][j+n-(1<<(k-1))][k-1],mn[i+n-(1<<(k-1))][j+n-(1<<(k-1))][k-1]);
mmn=min(t1,t2);
if(ans>mmx-mmn)ans=ans>mmx-mmn;
}
*/
修改,提交,全WA,修改代码如下
/*
for(i=1;i+n-1<=a;i++)
for(j=1;j+n-1<=b;j++){
int t1,t2,mmx,mmn;
t1=max(mx[i][j][k],mx[i+n-(1<
t1=min(mn[i][j][k],mn[i+n-(1<
if(ans>mmx-mmn)ans=ans>mmx-mmn;
}
*/
//继续排查,发现if(ans>mmx-mmn)ans=ans>mmx-mmn;
//样例通过,提交AC.2019-11-4 19:49
#include
#define maxn 1010
int a,b,n,mp,ans=2000000300,mx[maxn][maxn][10],mn[maxn][maxn][10];
int max(int a,int b){
return a>b?a:b;
}
int min(int a,int b){
return a }
int main(){
int i,j,k;
scanf("%d%d%d",&a,&b,&n);
for(i=1;i<=a;i++)
for(j=1;j<=b;j++)
scanf("%d",&mp),mx[i][j][0]=mn[i][j][0]=mp;
for(k=1;(1<
t1=max(mx[i][j][k-1],mx[i+(1<<(k-1))][j][k-1]);
t2=max(mx[i][j+(1<<(k-1))][k-1],mx[i+(1<<(k-1))][j+(1<<(k-1))][k-1]);
mx[i][j][k]=max(t1,t2);
t1=min(mn[i][j][k-1],mn[i+(1<<(k-1))][j][k-1]);
t2=min(mn[i][j+(1<<(k-1))][k-1],mn[i+(1<<(k-1))][j+(1<<(k-1))][k-1]);
mn[i][j][k]=min(t1,t2);
}
k--;
for(i=1;i+n-1<=a;i++)
for(j=1;j+n-1<=b;j++){
int t1,t2,mmx,mmn;
t1=max(mx[i][j][k],mx[i+n-(1<
t1=min(mn[i][j][k],mn[i+n-(1<
if(ans>mmx-mmn)ans=mmx-mmn;//此处错写成if(ans>mmx-mmn)ans=ans>mmx-mmn;
}
printf("%d\n",ans);
return 0;
}
方法五:二维RMQ 滚动数组优化
bzoj 1047
Accepted | 8792 kb | 2172 ms | C++/Edit | 1597 B |
//样例通过,提交AC.2019-11-4 19:57
#include
#define maxn 1010
int a,b,n,mp,ans=2000000300,mx[maxn][maxn],mn[maxn][maxn];
int max(int a,int b){
return a>b?a:b;
}
int min(int a,int b){
return a }
int main(){
int i,j,k;
scanf("%d%d%d",&a,&b,&n);
for(i=1;i<=a;i++)
for(j=1;j<=b;j++)
scanf("%d",&mp),mx[i][j]=mn[i][j]=mp;
for(k=1;(1<
t1=max(mx[i][j],mx[i+(1<<(k-1))][j]);
t2=max(mx[i][j+(1<<(k-1))],mx[i+(1<<(k-1))][j+(1<<(k-1))]);
mx[i][j]=max(t1,t2);
t1=min(mn[i][j],mn[i+(1<<(k-1))][j]);
t2=min(mn[i][j+(1<<(k-1))],mn[i+(1<<(k-1))][j+(1<<(k-1))]);
mn[i][j]=min(t1,t2);
}
k--;
for(i=1;i+n-1<=a;i++)
for(j=1;j+n-1<=b;j++){
int t1,t2,mmx,mmn;
t1=max(mx[i][j],mx[i+n-(1<
t1=min(mn[i][j],mn[i+n-(1<
if(ans>mmx-mmn)ans=mmx-mmn;//此处错写成if(ans>mmx-mmn)ans=ans>mmx-mmn;
}
printf("%d\n",ans);
return 0;
}
方法六:单调队列分别维护行与列 最容易上手的方法
//此文https://www.luogu.org/problemnew/solution/P2216 作者: chill 更新时间: 2018-03-08 21:58 思路不错,摘抄如下
//样例通过,提交AC.2019-11-4 22:12
#include
#define maxn 1010
int a,b,n,Q[maxn],H,T,q[maxn],h,t,ans=2000000100;
int mp[maxn][maxn],X[maxn][maxn],x[maxn][maxn],Y[maxn][maxn],y[maxn][maxn];
int min(int a,int b){
return a }
int main(){
int r,c;
scanf("%d%d%d",&a,&b,&n);
for(r=1;r<=a;r++)
for(c=1;c<=b;c++)
scanf("%d",&mp[r][c]);
for(r=1;r<=a;r++){
H=T=h=t=1,Q[T]=q[t]=1,T++,t++;
for(c=2;c<=b;c++){
while(H
Q[T]=q[t]=c,T++,t++;
while(H
while(h
if(c>=n)X[r][c-n+1]=mp[r][Q[H]],x[r][c-n+1]=mp[r][q[h]];//此处错写成if(Q[H]>=n)X[r][Q[H]-n+1]=mp[r][Q[H]];//此处错写成if(q[h]>=n)x[r][q[h]-n+1]=mp[r][q[h]];
}
}
for(c=1;c<=b-n+1;c++){
H=T=h=t=1,Q[T]=q[t]=1,T++,t++;
for(r=2;r<=a;r++){
while(H
Q[T]=q[t]=r,T++,t++;//此处错写成Q[T]=q[t]=c,T++,t++;
while(H
while(h
if(r>=n)Y[r-n+1][c]=X[Q[H]][c],y[r-n+1][c]=x[q[h]][c];
}
}
for(r=1;r<=a-n+1;r++)
for(c=1;c<=b-n+1;c++)
ans=min(ans,Y[r][c]-y[r][c]);
printf("%d\n",ans);
return 0;
}