luogu P2216 [HAOI2007]理想的正方形

** P2216 [HAOI2007]理想的正方形**
首先当然考虑暴力
确定左上角后就搜一遍整个正方形,时间复杂度O(abn^2),20pts

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int n,m,s;
int a[N][N];
int ans=2147483647;
int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
        }
    }
    for(int i=1;i+s-1<=n;i++){
        for(int j=1;j+s-1<=m;j++){
            //枚举左上角 
            int maxn=0,minn=2147483647;
            for(int k=i;k<=i+s-1;k++){
                for(int l=j;l<=j+s-1;l++){
                    maxn=max(maxn,a[k][l]);
                    minn=min(minn,a[k][l]);
                }
            }
            if(maxn-minn<ans) ans=maxn-minn;
        }
    }
    cout << ans << endl;
    return 0;
} 

20分够了,知足吧!
我们怎么能不思进取?
考虑优化
搞个前缀和一样的东西,与处理一下
时间复杂度O(abn) 70pts

#include<bits/stdc++.h>
using namespace std;
#define INF 2147483647
const int N=1005;
int n,m,s;
int a[N][N];
int f[N][N][2];//f[i][j][0,1]表示i,j点向下s个点之间最大值 /最小值 
int ans=INF;
int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
        }
    }
    for(int i=1;i+s-1<=n;i++){
        for(int j=1;j<=m;j++){
            //枚举左上角 
            f[i][j][0]=0;
            f[i][j][1]=INF;
            for(int k=i;k<=i+s-1;k++){
                f[i][j][0]=max(f[i][j][0],a[k][j]);
                f[i][j][1]=min(f[i][j][1],a[k][j]);
            }
        }
    }
    for(int i=1;i+s-1<=n;i++){
        for(int j=1;j+s-1<=m;j++){
            //枚举左上角 
            int maxn=0,minn=INF;
            for(int k=j;k<=j+s-1;k++){
                maxn=max(maxn,f[i][k][0]);
                minn=min(minn,f[i][k][1]);
            }
            if(maxn-minn<ans) ans=maxn-minn;
        }
    }
    cout << ans << endl;
    return 0;
} 

这是湖南的省选了,70分很不错了
我们再优化
考虑用线段树
由于是静态的,ST表其实更好
时间复杂度 O(ab log (max(a,b)))
100pts!
可惜我只有50pts(因为本人懒,不想打lazzy tag)

//超长版线段树做法 
#include<bits/stdc++.h>
using namespace std;
#define INF 2147483647
#define ri register int
const int N=1005;
int n,m,s;
int a[N][N];
int f[N][N][2];
int t[N*8][2];
int ans=INF;
void build1(int d,int l,int r,int sum){
    if(l==r){
        t[sum][0]=a[d][l];
        t[sum][1]=a[d][l];
        return;
    }
    int mid=(l+r)>>1;
    build1(d,l,mid,sum<<1);
    build1(d,mid+1,r,(sum<<1)+1);
    t[sum][0]=min(t[sum<<1][0],t[(sum<<1)+1][0]);
    t[sum][1]=max(t[sum<<1][1],t[(sum<<1)+1][1]);
    return;
}
int serch_min(int d,int dl,int dr,int l,int r,int sum){
    if(r<dl||l>dr) return INF;
    if(l>=dl&&r<=dr) return t[sum][0];
    int mid=(l+r)>>1;
    int n1=sum<<1,n2=(sum<<1)+1;
    return min(serch_min(d,dl,dr,l,mid,n1),serch_min(d,dl,dr,mid+1,r,n2));
}
int serch_max(int d,int dl,int dr,int l,int r,int sum){
    if(r<dl||l>dr) return 0;
    if(l>=dl&&r<=dr) return t[sum][1];
    
    int mid=(l+r)>>1;
    int n1=sum<<1,n2=(sum<<1)+1;
    return max(serch_max(d,dl,dr,l,mid,n1),serch_max(d,dl,dr,mid+1,r,n2));
}
void build212(int d,int l,int r,int sum){
    if(l==r){
        t[sum][0]=f[l][d][0];
        t[sum][1]=f[l][d][1];
        return;
    }
    int mid=(l+r)>>1;
    build212(d,l,mid,sum<<1);
    build212(d,mid+1,r,(sum<<1)+1);
    t[sum][0]=min(t[sum<<1][0],t[(sum<<1)+1][0]);
    t[sum][1]=max(t[sum<<1][1],t[(sum<<1)+1][1]);
    return;
}
int serch_minn(int d,int dl,int dr,int l,int r,int sum){
    if(r<dl||l>dr) return INF;
    if(l>=dl&&r<=dr) return t[sum][0];
    int mid=(l+r)>>1;
    int n1=sum<<1,n2=(sum<<1)+1;
    return min(serch_minn(d,dl,dr,l,mid,n1),serch_minn(d,dl,dr,mid+1,r,n2));
}
int serch_maxn(int d,int dl,int dr,int l,int r,int sum){
    if(r<dl||l>dr) return 0;
    if(l>=dl&&r<=dr) return t[sum][1];
    
    int mid=(l+r)>>1;
    int n1=sum<<1,n2=(sum<<1)+1;
    return max(serch_maxn(d,dl,dr,l,mid,n1),serch_maxn(d,dl,dr,mid+1,r,n2));
}
int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(ri i=1;i<=n;i++){
        for(ri j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
        } 
    }
    //第一次计算:计算f数组 
    for(ri i=1;i<=n;i++){
        memset(t,0,sizeof(t));
        build1(i,1,n,1);
        for(ri j=1;j+s-1<=m;j++){
            f[i][j][0]=serch_min(i,j,j+s-1,1,n,1);
            f[i][j][1]=serch_max(i,j,j+s-1,1,n,1);
        }
    }
    for(ri j=1;j+s-1<=m;j++){
        memset(t,0,sizeof(t));
        build212(j,1,n,1);
        for(ri i=1;i+s-1<=n;i++){
            int maxn,minn;
            minn=serch_minn(j,i,i+s-1,1,n,1);
            maxn=serch_maxn(j,i,i+s-1,1,n,1);
            ans=min(ans,maxn-minn);
        }
    }
    cout << ans << endl;
    return 0; 
} 

等等,刚那O(abn)算法有优化!
来!

#include<bits/stdc++.h>
using namespace std;
#define INF 2147483647
#define rg register
const int N=1005;
int n,m,s;
int a[N][N];
int f[N][N][2];//f[i][j][0,1]表示i,j点向下s个点之间最大值 /最小值 
int ans=INF;
int min(int a,int b){
    return a<b?a:b; 
} 
int max(int a,int b){
    return a>b?a:b;
}
int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(rg int i=1;i<=n;i++){
        for(rg int j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
        }
    }
    for(rg int i=1;i+s-1<=n;i++){
        for(rg int j=1;j<=m;j++){
            //枚举左上角 
            f[i][j][0]=0;
            f[i][j][1]=INF;
            for(int k=i;k<=i+s-1;k++){
                f[i][j][0]=max(f[i][j][0],a[k][j]);
                f[i][j][1]=min(f[i][j][1],a[k][j]);
            }
        }
    }
    for(rg int i=1;i+s-1<=n;i++){
        for(rg int j=1;j+s-1<=m;j++){
            //枚举左上角 
            int maxn=0,minn=INF;
            for(rg int k=j;k<=j+s-1;k++){
                maxn=max(maxn,f[i][k][0]);
                minn=min(minn,f[i][k][1]);
            }
            if(maxn-minn<ans) ans=maxn-minn;
        }
    }
    cout << ans << endl;
    return 0;
} 

虽然还是70pts,但是快了300ms!
再优化?
O(ab)可不可以?
可以!
单调队列!
首先用单调队列的思想预处理出上面程序的f数组
再用第二次单调队列计算正方形最大最小值
100pts
std:

#include<bits/stdc++.h>
using namespace std;
#define INF 2147483647
#define rg register
const int N=1005;
int n,m,s;
int a[N][N];
int f[N][N][2];//0小1大 
int ans=INF;
int h1,t1;//2小1大 
int q1[N]; 
int h2,t2;
int q2[N];
int min(int a,int b){
    return a<b?a:b; 
} 
int max(int a,int b){
    return a>b?a:b;
}
int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(rg int i=1;i<=n;i++){
        for(rg int j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
        }
    }
    for(rg int j=1;j<=m;j++){
     h1=1,t1=0;
     h2=1,t2=0;
     for(rg int i=1;i<s;i++){
      while(h1<=t1&&a[q1[t1]][j]<=a[i][j]) t1--;
      q1[++t1]=i;
      while(h2<=t2&&a[q2[t2]][j]>=a[i][j]) t2--;
      q2[++t2]=i;
  }
        for(rg int i=s;i<=n;i++){
            while(h1<=t1&&a[q1[t1]][j]<=a[i][j]) t1--;
      while(h1<=t1&&q1[h1]<i-s+1) h1++;
      q1[++t1]=i;
      f[i-s+1][j][1]=a[q1[h1]][j];
      
      while(h2<=t2&&a[q2[t2]][j]>=a[i][j]) t2--;
      while(h2<=t2&&q2[h2]<i-s+1) h2++;
      q2[++t2]=i;
      f[i-s+1][j][0]=a[q2[h2]][j];
        }
    }
    for(rg int i=1;i+s-1<=n;i++){
        h1=1,t1=0;
     h2=1,t2=0;
     for(rg int j=1;j<s;j++){
      while(h1<=t1&&f[i][q1[t1]][1]<=f[i][j][1]) t1--;
      q1[++t1]=j;
      while(h2<=t2&&f[i][q2[t2]][0]>=f[i][j][0]) t2--;
      q2[++t2]=j;
  }
        for(rg int j=s;j<=m;j++){
            while(h1<=t1&&f[i][q1[t1]][1]<=f[i][j][1]) t1--;
      while(h1<=t1&&q1[h1]<j-s+1) h1++;
      q1[++t1]=j;
      int maxn=f[i][q1[h1]][1];
      
      while(h2<=t2&&f[i][q2[t2]][0]>=f[i][j][0]) t2--;
      while(h2<=t2&&q2[h2]<j-s+1) h2++;
      q2[++t2]=j;
      int minn=f[i][q2[h2]][0];
      ans=min(ans,maxn-minn);
        }
    }
    cout << ans << endl;
    return 0;
} 

一道水题硬生生做成了难题
我是疯了不是

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值