【BZOJ 1047】【HAOI 2007】 理想的正方形 / 单调队列

本文介绍一种高效算法,用于解决给定n×m矩阵中寻找极差最小的k×k子矩阵问题。通过使用单调队列进行降维处理,实现对静态二维区间极值的有效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简述:给定 n×mn×m的矩阵,求其所有k×kk×k的子矩阵中,极差最小的一个。(PS:极差=最大值-最小值)
其中 n,m1000n,m≤1000k100k≤100

一个静态二维区间极值问题。可以选择带 loglog 的数据结构维护,不过由于是静态,杀鸡就不用牛刀了,上单调队列要更快。(有点二维滑动窗口的感觉)
核心思路就是降维,我们不妨把一维压掉,只保留有用信息,比如我们将每一列(纵的叫列)存成一个数,表示该列的最大/小值(因为只有这俩有用啊)
我们先枚举行,从 11n+k1
然后考虑每次枚举时的状态,此时是一个 k×mk×m 的矩阵,我们将每一列存下最大值和最小值,于是就得到两个数组 Maxi,MiniMaxi,Mini,长度均为 mm,接下来的问题就是对这两个一维的数组上求连续段极值,用单调队列维护。
时间复杂度 O(n(m+k)),代码见下:

#include<bits/stdc++.h> //万能头文件并不支持所有OJ
using namespace std;
#define maxa 1002
#define R register
#define INF 2147483000
int a,b,n,ans=INF,G[maxa][maxa],min1[maxa],max1[maxa];
struct fifo
{
    int l,r,q[maxa];
    fifo(){l=r=0;memset(q,0,sizeof q);}
    void cls() {l=r=0;}
    void max_insert(int pos) {while(!empty()&&max1[q[r-1]]<=max1[pos])--r;q[r++]=pos;}
    void min_insert(int pos) {while(!empty()&&min1[q[r-1]]>=min1[pos])--r;q[r++]=pos;}  
    void del(int pos) {while(!empty()&&front()<=pos)pop();}
    void pop() {++l;}
    int front() {return q[l];}
    bool empty() {return l>=r;}
}maxq,minq;
int main()
{
    scanf("%d %d %d",&a,&b,&n);
    for (R int i=1;i<=a;++i)for (R int j=1;j<=b;++j)scanf("%d",&G[i][j]);
    for (R int i=1;i+n-1<=a;++i)
    {
        maxq.cls();minq.cls();
        for (R int j=1;j<=b;++j)
        {
            min1[j]=INF;max1[j]=-INF;
            for (R int k=i;k<=i+n-1;++k) max1[j]=max(max1[j],G[k][j]),min1[j]=min(min1[j],G[k][j]);
            maxq.del(j-n);maxq.max_insert(j); 
            minq.del(j-n);minq.min_insert(j);
            (j >= n) ? ans=min(ans,max1[maxq.front()]-min1[minq.front()]) : 0;
        }
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值