[BZOJ 1047][HAOI 2007]理想的正方形(二维滑动窗口+单调队列)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1047

思路:裸的二维上的滑动窗口问题,可以借鉴一维滑动窗口的思路。首先预处理出每一列j的、以第i行元素为结尾、长度为n的区间的最大值maxv[i][j]、最小值minv[i][j],然后再搞每一行,求出以每一行i结尾、行标上长度为n的区间、以第j列结尾、列标上长度为n的区间得到的二维区间最大值与最小值之差,遍历每一行得到这个差的最小值即为答案。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stdlib.h>

#define MAXN 1200
#define INF 0x3f3f3f3f

using namespace std;

struct node
{
    int val; //这个元素的值
    int pos; //这个元素的下标
}q1[MAXN],q2[MAXN]; //q1维护一个单调递减队列,q2维护一个单调递增对了队列

int map[MAXN][MAXN];
int maxv[MAXN][MAXN],minv[MAXN][MAXN];
int pa,pb,n; //pa*pb大小的矩阵,从中取n*n大小的子矩阵

void init()
{
    scanf("%d%d%d",&pa,&pb,&n);
    for(int i=1;i<=pa;i++)
        for(int j=1;j<=pb;j++)
            scanf("%d",&map[i][j]);
}

void work1() //第一次操作维护每一列的最值,最终得到第j列以第i个元素结尾的长度为n的区间的最大值、最小值
{
    int h1,t1,h2,t2; //队尾与队首的指针
    for(int j=1;j<=pb;j++) //枚举列号j
    {
        h1=h2=t1=t2=0;
        for(int i=1;i<=pa;i++) //枚举长度为n的区间的最后一个元素下标i,当前长度为n的区间为[i-n+1,i
        {
            //维护队列q1
            while(h1<t1&&q1[h1+1].pos<=i-n) //把队首不属于当前区间的都弹掉
                h1++;
            while(h1<t1&&q1[t1].val<=map[i][j]) //把队尾不满足单调性的都弹掉
                t1--;
            q1[++t1].val=map[i][j];
            q1[t1].pos=i;
            //维护队列q2
            while(h2<t2&&q2[h2+1].pos<=i-n) //把队首不属于当前区间的都弹掉
                h2++;
            while(h2<t2&&q2[t2].val>=map[i][j]) //把队尾不满足单调性的都弹掉
                t2--;
            q2[++t2].val=map[i][j];
            q2[t2].pos=i;
            if(i<n) //区间里元素个数不够
            {
                maxv[i][j]=INF;
                minv[i][j]=-INF;
            }
            else
            {
                maxv[i][j]=q1[h1+1].val;
                minv[i][j]=q2[h2+1].val;
            }
        }
    }
}

void work2() //第二次操作在第一次的基础上,遍历每一行,找出每一行i结尾的、以第j列结尾的长度为n的区间的最大值中的最小值、最小值中的最大值
{
    int ans=INF;
    int h1,t1,h2,t2; //队尾与队首的指针
    for(int i=1;i<=pa;i++) //枚举行号i
    {
        h1=h2=t1=t2=0;
        for(int j=1;j<=pb;j++) //枚举长度为n的区间的最后一个元素下标j,当前长度为n的区间为[i-n+1,i
        {
            //维护队列q1
            while(h1<t1&&q1[h1+1].pos<=j-n) //把队首不属于当前区间的都弹掉
                h1++;
            while(h1<t1&&q1[t1].val<=maxv[i][j]) //把队尾不满足单调性的都弹掉
                t1--;
            q1[++t1].val=maxv[i][j];
            q1[t1].pos=j;
            //维护队列q2
            while(h2<t2&&q2[h2+1].pos<=j-n) //把队首不属于当前区间的都弹掉
                h2++;
            while(h2<t2&&q2[t2].val>=minv[i][j]) //把队尾不满足单调性的都弹掉
                t2--;
            q2[++t2].val=minv[i][j];
            q2[t2].pos=j;
            if(i>=n&&j>=n)
                ans=min(ans,q1[h1+1].val-q2[h2+1].val);
        }
    }
    printf("%d\n",ans);
}

int main()
{
    init();
    work1();
    work2();
    return 0;
}



时间: 2024-10-10 21:27:55

[BZOJ 1047][HAOI 2007]理想的正方形(二维滑动窗口+单调队列)的相关文章

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

题目大意:给出一个矩阵,求出一个k*k的子矩阵,使得这个矩阵中最大值和最小值的差最小,输出这个差值. 思路:利用单调队列维护每一行的数字,求出一个数字前面k个数字中的最大值和最小值,然后在列上暴力求出真个矩阵的最大值和最小值,总时间复杂度O(M*M+M*M*K). CODE: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algor

【BZOJ 1047】 [HAOI2007]理想的正方形

1047: [HAOI2007]理想的正方形 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 1685  Solved: 891 [Submit][Status] Description 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. Input 第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每行相邻两数之间用一空格分隔.

1047: [HAOI2007]理想的正方形——二维单调队列

http://www.lydsy.com/JudgeOnline/problem.php?id=1047 对每一行维护一个单调队列,保存在lmin[][],lmax[][] 然后对每一列维护一个单调队列,最后n*n枚举 #include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #define clr(a,b) memset(a,b,sizeof(a)) cons

【bzoj1047】[HAOI2007]理想的正方形 二维RMQ

题目描述 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. 输入 第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每行相邻两数之间用一空格分隔.100%的数据2<=a,b<=1000,n<=a,n<=b,n<=1000 输出 仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值. 样例输入 5 4 2 1 2 5 6 0 1

微信小程序(4)--二维码窗口

微信小程序二维码窗口: <view class="btn" bindtap="powerDrawer" data-statu="open">button</view> <!--mask--> <view class="drawer_screen" bindtap="powerDrawer" data-statu="close" wx:if=&qu

11.4 “5图形”二维滑动门经典圆角框

11.3节介绍的不固定宽度的圆角框制作方法只适用于单色的圆角框,对于带有复杂边框的圆角框就不适用了. 另外,还需要考虑一个问题,用11.3节介绍的方法制作的圆角框,尽管可以适应不同宽度,但是它里面的正文段落也承担了设置圆角框布局的任务.也就是说,圆角框中的正文并不是独立的,正文内容不能自由地设置样式.这对于通用型的网页来说,局限性很大:例如,如果较为复杂的页面,每个圆角框用于放置不同的栏目,希望达到的目标应该是可以在每个圆角框中放置任何内容,并且可以对这些内容设置样式,还能保证圆角框本身显示正确

BZOJ 1567 Blue Mary的战役地图(二维hash+二分)

题意: 求两个矩形最大公共子正方形.(n<=50) 范围这么小可以枚举子正方形的边长.那么可以对这个矩形进行二维hash,就可以在O(1)的时候求出任意子矩形的hash值.然后判断这些正方形的hash值有没有相同的 部分就行了.可以用二分来判断. 需要注意的是行和列乘的hash种子值需要不同的质数,否则可能出现冲突. 时间复杂度O(n^3logn). # include <cstdio> # include <cstring> # include <cstdlib>

BZOJ 3132: 上帝造题的七分钟( 二维BIT )

二维树状数组... 自己YY一下再推一下应该可以搞出来... ---------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep( i , n ) for( int i = 0 ; i <

BZOJ 2738 矩阵乘法(整体二分+二维树状数组)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2738 [题目大意] 给出一个方格图,询问要求求出矩阵内第k小的元素 [题解] 我们对答案的大小进行整体二分,用二维树状数组维护二维区间和, 将超过数量的分治到左区间,不满足的分治到右区间即可. [代码] #include <cstdio> #include <algorithm> #include <cstring> using namespace std;