bzoj 1047 //1047: [HAOI2007]理想的正方形 朴素算法/动归/动归 滚动数组优化/二维RMQ/二维RMQ 滚动数组优化/单调队列分别维护行与列

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 <stdio.h>
#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<b?a:b;
}
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 <stdio.h>
#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<b?a:b;
}
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 <stdio.h>
#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<b?a:b;
}
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<<k)<=n;k++)
        for(i=1;i+(1<<k)-1<=a;i++)
            for(j=1;j+(1<<k)-1<=b;j++){
                int t1,t2;
                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<<k)<=n;k++)
        for(i=1;i+(1<<k)-1<=a;i++)
            for(j=1;j+(1<<k)-1<=b;j++){
                int t1,t2;
                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<<k)][j][k]);//不用上面循环方法的原因是,上面算法,可能会超越所求矩阵范围
            t2=max(mx[i][j+n-(1<<k)][k],mx[i+n-(1<<k)][j+n-(1<<k)][k]);
            mmx=max(t1,t2);
            t1=min(mn[i][j][k],mn[i+n-(1<<k)][j][k]);//不用上面循环方法的原因是,上面算法,可能会超越所求矩阵范围
            t2=min(mn[i][j+n-(1<<k)][k],mn[i+n-(1<<k)][j+n-(1<<k)][k]);
            mmn=min(t1,t2);
            if(ans>mmx-mmn)ans=ans>mmx-mmn;
        }
*/
//继续排查,发现if(ans>mmx-mmn)ans=ans>mmx-mmn;
//样例通过,提交AC.2019-11-4 19:49
#include <stdio.h>
#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<b?a:b;
}
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<<k)<=n;k++)
        for(i=1;i+(1<<k)-1<=a;i++)
            for(j=1;j+(1<<k)-1<=b;j++){
                int t1,t2;
                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<<k)][j][k]);//不用上面循环方法的原因是,上面算法,可能会超越所求矩阵范围
            t2=max(mx[i][j+n-(1<<k)][k],mx[i+n-(1<<k)][j+n-(1<<k)][k]);
            mmx=max(t1,t2);
            t1=min(mn[i][j][k],mn[i+n-(1<<k)][j][k]);//不用上面循环方法的原因是,上面算法,可能会超越所求矩阵范围
            t2=min(mn[i][j+n-(1<<k)][k],mn[i+n-(1<<k)][j+n-(1<<k)][k]);
            mmn=min(t1,t2);
            if(ans>mmx-mmn)ans=mmx-mmn;//此处错写成if(ans>mmx-mmn)ans=ans>mmx-mmn;
        }
    printf("%d\n",ans);
    return 0;
}

方法五:二维RMQ   滚动数组优化

bzoj 1047

Accepted8792 kb2172 msC++/Edit1597 B

//样例通过,提交AC.2019-11-4 19:57
#include <stdio.h>
#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<b?a:b;
}
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<<k)<=n;k++)
        for(i=1;i+(1<<k)-1<=a;i++)
            for(j=1;j+(1<<k)-1<=b;j++){
                int t1,t2;
                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<<k)][j]);//不用上面循环方法的原因是,上面算法,可能会超越所求矩阵范围
            t2=max(mx[i][j+n-(1<<k)],mx[i+n-(1<<k)][j+n-(1<<k)]);
            mmx=max(t1,t2);
            t1=min(mn[i][j],mn[i+n-(1<<k)][j]);//不用上面循环方法的原因是,上面算法,可能会超越所求矩阵范围
            t2=min(mn[i][j+n-(1<<k)],mn[i+n-(1<<k)][j+n-(1<<k)]);
            mmn=min(t1,t2);
            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 <stdio.h>
#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<b?a:b;
}
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<T&&mp[r][Q[T-1]]<=mp[r][c])T--;
            while(h<t&&mp[r][q[t-1]]>=mp[r][c])t--;
            Q[T]=q[t]=c,T++,t++;
            while(H<T&&Q[T-1]-Q[H]+1>n)H++;//此处错写成while(Q[T-1]-Q[H]>=n)H++;
            while(h<t&&q[t-1]-q[h]+1>n)h++;//此处错写成while(q[t-1]-q[h]>=n)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<T&&X[Q[T-1]][c]<=X[r][c])T--;
            while(h<t&&x[q[t-1]][c]>=x[r][c])t--;
            Q[T]=q[t]=r,T++,t++;//此处错写成Q[T]=q[t]=c,T++,t++;
            while(H<T&&Q[T-1]-Q[H]+1>n)H++;
            while(h<t&&q[t-1]-q[h]+1>n)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;
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值