BZOJ1047 - [HAOI2007]理想的正方形

原题链接

题意简述

有一个由 n×m 个整数组成的矩阵,现请你从中找出一个 k×k 的方阵,使得该方阵所有数中的最大值和最小值的差最小。

分析

首先处理出 c1[i][j] 表示 max{a[i][j..(j+k1)]} c2[i][j] 表示 min{a[i][j..(j+k1)]} 。也就是从 (i,j) 向右连续 k 个数中的最值。
然后以(i,j)为左上角的 k×k 方阵中的最大值就是 max{c1[i..(i+k1)][j]} ,最小值就是 min{c2[i..(i+k1)][j]} 。因为 (i,j) 向右 k 个数加上(i+1,j)向右 k 个数加上…加上(i+k1,j)向右 k 个数就是以(i,j)为左上角的 k×k 方阵。所有最大值与最小值的差中的最小值就是答案。
以上都可以通过单调队列实现。
总时间复杂度 O(nm)

实现

开两个单调队列,一个递增,一个递减。

代码

//[HAOI2007]理想的正方形
#include <cstdio>
#include <algorithm>
using namespace std;
int const N=1e3+10;
int const INF=0x7FFFFFFF;
int n,m,k,a[N][N];
int c[2][N][N];
struct queue
{
    int op,cl,type;
    struct ele{int x,y,val;} qu[N];
    void clear() {op=1,cl=0;}
    void push(int x,int y,int val)
    {
        if(type==0) while(qu[cl].val<=val && cl>=op) cl--;
        else while(qu[cl].val>=val && cl>=op) cl--;
        cl++; qu[cl].x=x,qu[cl].y=y,qu[cl].val=val;
    }
    int topX() {return qu[op].x;}
    int topY() {return qu[op].y;}
    int topV() {return qu[op].val;}

}q[2];
void push1(int x,int y) {q[0].push(x,y,a[x][y]); q[1].push(x,y,a[x][y]);}
void push2(int x,int y) {q[0].push(x,y,c[0][x][y]); q[1].push(x,y,c[1][x][y]);}
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",&a[i][j]);
    q[0].type=0; q[1].type=1;
    for(int i=1;i<=n;i++)
    {
        q[0].clear(); q[1].clear();
        for(int j=1;j<=k;j++) push1(i,j);
        c[0][i][1]=q[0].topV();
        c[1][i][1]=q[1].topV();
        for(int j=2;j<=m-k+1;j++)  
        {
            if(q[0].topY()<j) q[0].op++;
            if(q[1].topY()<j) q[1].op++;
            push1(i,j+k-1);
            c[0][i][j]=q[0].topV();
            c[1][i][j]=q[1].topV();
        }
    }
    int ans=INF;
    for(int j=1;j<=m-k+1;j++)
    {
        q[0].clear(); q[1].clear();
        for(int i=1;i<=k;i++) push2(i,j);
        ans=min(ans,q[0].topV()-q[1].topV());
        for(int i=2;i<=n-k+1;i++)
        {
            if(q[0].topX()<i) q[0].op++;
            if(q[1].topX()<i) q[1].op++;
            push2(i+k-1,j);
            ans=min(ans,q[0].topV()-q[1].topV());
        }
    }
    printf("%d",ans);
    return 0;
}

注意

代码可能很难写的简洁…我尽力了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值