AcWing 1091. 理想的正方形

孤者浪人 提交于 2019-12-04 08:46:50

题目描述

有一个 a×b 的整数组成的矩阵,现请你从中找出一个 n×n 的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

输入样例

5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2

输出样例

1

题目分析

这道题一看,发现很像滑动窗口,只不过变成了二维的.
所以就是一个二维滑动窗口.

前置知识

既然是二维的滑动窗口,那么我们就必须先会一维.也就是一维单调队列.
那么就在此简单的讲一下单调队列的实现:
以最大值为例,既然我们想要保证队列开头为答案,那么我们就要保证每次更新使最大值一直放在队列。
当前队头就是目前最大值,那么如果我们要求进入一个新的值的时候,应该怎么办呢?
1.可以想,我们的区间长度是不超过k的那么如果当前队列里和我目前将要进队的这个值的位置相差超过了k那么就得出队。
2.还有一种情况,因为队列是从大到小排序的,最新的值又是从队尾入的,所以我们现在要保持单调性的话,就必须把对尾的所有小于现在数给踢掉.

参考代码
int k;
int a[N],q[N];
int head,tail;
head=tail=0;
for(int i=1;i<=n;++i){
    while(head<=tail&&i-q[head]>k) head++;
    while(head<=tail&&a[i]>=a[q[tail]]) tail--;
    q[++tail]=i;
}

ok!
既然知道了一维的,那么二维的就迎刃而解了.
同时处理行和列是不可能的,那么我们就先单独对列处理.用两个队列和f[i][j]和g[i][j]分别存储以i,j结尾的大小为k的矩阵的最大与最小值,然后通过一维的滑动窗口处理出每一列的极值,然后在对每一行进行处理.
最后统计答案.
完结散花✿✿ヽ(°▽°)ノ✿

#include<bits/stdc++.h>
using namespace std;
const int INF=1e9;
const int N=1e3+50;
int n,m,k,ans;
int head1,tail1,head2,tail2;
int a[N][N],f[N][N],g[N][N];
struct data {
    int x,s;
} q1[N],q2[N];
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]);
    for(int i=1; i<=n; i++) {
        head1=tail1=1;
        head2=tail2=1;
        q1[1].x=1;
        q1[1].s=a[i][1];
        q2[1].x=1;
        q2[1].s=a[i][1];
        for(int j=1; j<=m; j++) {
            while(head1<=tail1&&q1[head1].x<=j-k) head1++;
            while(head2<=tail2&&q2[head2].x<=j-k) head2++;
            f[i][j]=q1[head1].s;
            g[i][j]=q2[head2].s;
            while(head1<=tail1&&q1[tail1].s<=a[i][j+1]) tail1--;
            while(head2<=tail2&&q2[tail2].s>=a[i][j+1]) tail2--;
            q1[++tail1].x=j+1;
            q1[tail1].s=a[i][j+1];
            q2[++tail2].x=j+1;
            q2[tail2].s=a[i][j+1];
        }
    }
    ans=INF<<1;
    for(int j=k; j<=m; j++) {
        head1=tail1=1;
        head2=tail2=1;
        q1[1].x=1;
        q1[1].s=f[1][j];
        q2[1].x=1;
        q2[1].s=g[1][j];
        for(int i=1; i<=n; i++) {
            while(head1<=tail1&&q1[head1].x<=i-k) head1++;
            while(head2<=tail2&&q2[head2].x<=i-k) head2++;
            if(i>=k) ans=min(ans,q1[head1].s-q2[head2].s);
            while(head1<=tail1&&q1[tail1].s<=f[i+1][j]) tail1--;
            while(head2<=tail2&&q2[tail2].s>=g[i+1][j]) tail2--;
            q1[++tail1].x=i+1;
            q1[tail1].s=f[i+1][j];
            q2[++tail2].x=i+1;
            q2[tail2].s=g[i+1][j];
        }
    }
    printf("%d",ans);
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!