单调队列

发个昨天考试的题 二维单调队列
单调队列之前也学了但没做过题,没写过。但我感觉也不难,今天直接搞了个二维的;
发个一维的讲解,不会的先看一下,以便看懂下面的题解。点这里
T1 为了和谐
(square.pas/c/cpp)
[问题背景]
在机房里,有两只小可爱,一只大可爱,一只小可爱。其中大可
爱对大的东西感兴趣,小可爱对小的东西感兴趣。
现在有一个 a*b 的矩阵,矩阵中每个位置都有一个非负整数 i
( 0<=i<=2000 000 000),两只小可爱要在这个矩阵中选择一个 n*n 的
正方形,大可爱要正方形中的最大数,小可爱要正方形中的最小数。
但是,如果两个人的数字差距过大的话,就会造成矛盾……为了
机房人民的和谐相处,请你帮他们选择这个正方形,使得这个差距最
小。
[问题描述]
有一个 a*b 的整数组成的矩阵,现请你从中找出一个 n*n 的正
方形区域,使得该区域所有数中的最大值和最小值的差最小。
INPUT (square.in)
第一行 3 个整数 a b n
接下来 a 行,每行 b 个非负整数。
OUTPUT (square.out)
仅一个整数,表示正方形中最大数与最小数之差[样例输入]
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
[样例输出]
1
HINT
1, 矩阵中所有数小于 2000 000 000
2, 20%数据, 2<=a,b<=100, n<=a, n<=b, n<=10
3, 100%数据, 2<=a,b<=1000, n<=a, n<=b, n<=100

先每行搞单调队列,一个最大一个最小。然后对应存起来这个位置区间的最值。再按列搞,搞出来就是矩阵的最值。
代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1010
using namespace std;

struct M{
    int pos,x;
}maxn[N],minn[N];
int mar[N][N],ma[N][N],mi[N][N],n,a,b,ha,hi,ta,ti,ans;
int main()
{
    freopen("square.in","r",stdin);
    freopen("square.out","w",stdout);
    scanf("%d%d%d",&a,&b,&n);
    for(int i=1;i<=a;++i)
     for(int j=1;j<=b;++j)scanf("%d",&mar[i][j]);
    for(int i=1;i<=a;++i)
    {
        ha=hi=1;ta=ti=0;
     for(int j=1;j<=b;++j)
     {
        if(ha<=ta&&maxn[ha].pos<j-(n-1))ha++;
        if(hi<=ti&&minn[hi].pos<j-(n-1))hi++;
        while(ha<=ta&&mar[i][j]>maxn[ta].x)ta--;
        maxn[++ta].x=mar[i][j],maxn[ta].pos=j;
        while(hi<=ti&&mar[i][j]<minn[ti].x)ti--;
        minn[++ti].x=mar[i][j],minn[ti].pos=j;
        if(j-(n-1)>0)ma[i][j-(n-1)]=maxn[ha].x,
         mi[i][j-(n-1)]=minn[hi].x;
     }
    }
    for(int j=1;j<=b-(n-1);++j)
    {
    ha=hi=1;ta=ti=0;
     for(int i=1;i<=a;++i)
     {  
        if(ha<=ta&&maxn[ha].pos<i-(n-1))ha++;
        if(hi<=ti&&minn[hi].pos<i-(n-1))hi++;
        while(ha<=ta&&ma[i][j]>maxn[ta].x)ta--;
        maxn[++ta].x=ma[i][j],maxn[ta].pos=i;
        while(hi<=ti&&mi[i][j]<minn[ti].x)ti--;
        minn[++ti].x=mi[i][j],minn[ti].pos=i;
        if(i-(n-1)>0)ma[i-(n-1)][j]=maxn[ha].x,
         mi[i-(n-1)][j]=minn[hi].x;
     } 
    } 
    ans=0x7fffffff;
    for(int i=1;i<=a-(n-1);++i)
     for(int j=1;j<=b-(n-1);++j)
    ans=min(ans,ma[i][j]-mi[i][j]); 
    printf("%d",ans);
    return 0;   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值