P2216 [HAOI2007]理想的正方形 方法记录
[HAOI2007]理想的正方形
题目描述
有一个 \(a \times b\) 的整数组成的矩阵,现请你从中找出一个 \(n \times n\) 的正方形区域,使得该区域所有数中的最大值和最小值的差最小。
输入格式
第一行为 \(3\) 个整数,分别表示 \(a,b,n\) 的值。
第二行至第 \(a+1\) 行每行为 \(b\) 个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。
输出格式
仅一个整数,为 \(a \times b\) 矩阵中所有“ \(n \times n\) 正方形区域中的最大整数和最小整数的差值”的最小值。
样例 #1
样例输入 #1
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
提示
问题规模。
矩阵中的所有数都不超过 \(1,000,000,000\)。
\(20\%\) 的数据 \(2 \le a,b \le 100,n \le a,n \le b,n \le 10\)。
\(100\%\) 的数据 \(2 \le a,b \le 1000,n \le a,n \le b,n \le 100\)。
题解
前置知识
试想一下,如果我们把\(a*b\)的矩阵改为长度为\(a\)的序列,要找的东西变成长度为\(n\)的子段,那不就变成单调队列之滑动窗口了嘛。
可以先看看我写的这篇关于滑动窗口的博客。
有了这些对单调队列的基本认知,我们不难想出一种本题的做法。
方法分析
首先可以将\(a*b\)的矩阵理解为\(b\)行长度为\(a\)的序列,然后对每一行的序列使用“滑动窗口”,这样就可以处理出每一行中长度为\(n\)的子段中所有数的最大值和最小值。
我们用\(board[i][j]\)来存原来的矩阵,用\(x1[i][j]\)和\(x2[i][j]\)分别记录第\(i\)行第\(j\)个窗口的最小值和最大值。那么我们每一行就能产生\(b-n+1\)个窗口。处理出来的\(x1\)和\(x2\)规模就是\(a*(b-n+1)\).
for(int i=1;i<=a;i++)
{
int h=0,t=0;
memset(q1,0,sizeof(q1));
memset(q2,0,sizeof(q2));
for(int j=1;j<=b;j++)
{
while(h<=t&&j-q1[h]>=n) h++;
while(h<=t&&board[i][j]<board[i][q1[t]]) t--;
q1[++t]=j;
if(j>=n) x1[i][j-n+1]=board[i][q1[h]];
}
for(int j=1;j<=b;j++)
{
while(h<=t&&j-q2[h]>=n) h++;
while(h<=t&&board[i][j]>board[i][q2[t]]) t--;
q2[++t]=j;
if(j>=n) x2[i][j-n+1]=board[i][q2[h]];
}
}
然后我们在处理好的\(x1\)和\(x2\)基础上,对每一列使用“滑动窗口”。如果说每一行的滑动窗口是从左往右滑动的,那么每一列的滑动窗口就是从上往下滑动的。
我们用用\(y1[i][j]\)和\(y2[i][j]\)分别记录第\(i\)列第\(j\)个窗口的最小值和最大值。那么我们每一列就能产生\(a-n+1\)个窗口。处理出来的\(y1\)和\(y2\)规模就是\((a-n+1)*(b-n+1)\).
for(int i=1;i<=b-n+1;i++)
{
int h=0,t=0;
memset(q1,0,sizeof(q1));
memset(q2,0,sizeof(q2));
for(int j=1;j<=a;j++)
{
while(h<=t&&j-q1[h]>=n) h++;
while(h<=t&&x1[j][i]<x1[q1[t]][i]) t--;
q1[++t]=j;
if(j>=n) y1[j-n+1][i]=x1[q1[h]][i];
}
for(int j=1;j<=a;j++)
{
while(h<=t&&j-q2[h]>=n) h++;
while(h<=t&&x2[j][i]>x2[q2[t]][i]) t--;
q2[++t]=j;
if(j>=n) y2[j-n+1][i]=x2[q2[h]][i];
}
}
回想一下,\(x\)数组处理出的是每一行长度为\(n\)的子段中最小/最大值,\(y\)数组处理出的是\(x\)的基础上每一列长度为\(n\)的子段中最小/最大值,那么这样一来\(y\)数组中就是整个矩阵中\(n*n\)的正方形区域中的最小/最大值。
然后只需要遍历一遍,求出最小的\(y2[i][j]-y1[i][j]\)即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1005;
int a,b,n;
int board[N][N];
int x1[N][N],x2[N][N],y1[N][N],y2[N][N];
int q1[N],q2[N];
int cntx1,cntx2,cnty1,cnty2;
int minn(int a,int b)
{
return a<b?a:b;
}
int ans=2147483647;
int main()
{
scanf("%d%d%d",&a,&b,&n);
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
scanf("%d",&board[i][j]);
for(int i=1;i<=a;i++)
{
int h=0,t=0;
memset(q1,0,sizeof(q1));
memset(q2,0,sizeof(q2));
for(int j=1;j<=b;j++)
{
while(h<=t&&j-q1[h]>=n) h++;
while(h<=t&&board[i][j]<board[i][q1[t]]) t--;
q1[++t]=j;
if(j>=n) x1[i][j-n+1]=board[i][q1[h]];
}
for(int j=1;j<=b;j++)
{
while(h<=t&&j-q2[h]>=n) h++;
while(h<=t&&board[i][j]>board[i][q2[t]]) t--;
q2[++t]=j;
if(j>=n) x2[i][j-n+1]=board[i][q2[h]];
}
}
memset(q1,0,sizeof(q1));
memset(q2,0,sizeof(q2));
for(int i=1;i<=b-n+1;i++)
{
int h=0,t=0;
memset(q1,0,sizeof(q1));
memset(q2,0,sizeof(q2));
for(int j=1;j<=a;j++)
{
while(h<=t&&j-q1[h]>=n) h++;
while(h<=t&&x1[j][i]<x1[q1[t]][i]) t--;
q1[++t]=j;
if(j>=n) y1[j-n+1][i]=x1[q1[h]][i];
}
for(int j=1;j<=a;j++)
{
while(h<=t&&j-q2[h]>=n) h++;
while(h<=t&&x2[j][i]>x2[q2[t]][i]) t--;
q2[++t]=j;
if(j>=n) y2[j-n+1][i]=x2[q2[h]][i];
}
}
for(int i=1;i<=a-n+1;i++)
for(int j=1;j<=b-n+1;j++)
ans=minn(ans,y2[i][j]-y1[i][j]);
printf("%d\n",ans);
return 0;
}
P2216 [HAOI2007]理想的正方形 方法记录的更多相关文章
- 洛谷 P2216 [HAOI2007]理想的正方形
P2216 [HAOI2007]理想的正方形 题目描述 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. 输入输出格式 输入格式: 第一 ...
- P2216 [HAOI2007]理想的正方形 (单调队列)
题目链接:P2216 [HAOI2007]理想的正方形 题目描述 有一个 \(a\times b\)的整数组成的矩阵,现请你从中找出一个 \(n\times n\)的正方形区域,使得该区域所有数中的最 ...
- P2216 [HAOI2007]理想的正方形
题目描述 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. 输入输出格式 输入格式: 第一行为3个整数,分别表示a,b,n的值 第二行至 ...
- 洛谷 P2216 [HAOI2007]理想的正方形 || 二维RMQ的单调队列
题目 这个题的算法核心就是求出以i,j为左上角,边长为n的矩阵中最小值和最大值.最小和最大值的求法类似. 单调队列做法: 以最小值为例: q1[i][j]表示第i行上,从j列开始的n列的最小值.$q1 ...
- [P2216] [HAOI2007]理想的正方形 「单调队列」
思路:用单调队列分别维护行与列. 具体实现方法:是先用单调队列对每一行的值维护,并将a[][]每个区间的最大值,最小值分别存在X[][]和x[][]中. 那么X[][]与x[][]所存储的分别是1×n ...
- 洛谷P2216 HAOI2007 理想的正方形 (单调队列)
题目就是要求在n*m的矩形中找出一个k*k的正方形(理想正方形),使得这个正方形内最值之差最小(就是要维护最大值和最小值),显然我们可以用单调队列维护. 但是二维平面上单调队列怎么用? 我们先对行处理 ...
- 【DP】【单调队列】洛谷 P2216 [HAOI2007]理想的正方形 题解
算是单调队列的复习吧,不是很难 题目描述 有一个$a\times b$的整数组成的矩阵,现请你从中找出一个$n\times n$的正方形区域,使得该区域所有数中的最大值和最小值的差最小. 输入 ...
- P2216 [HAOI2007]理想的正方形(二维RMQ)
题目描述 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. 输入输出格式 输入格式: 第一行为3个整数,分别表示a,b,n的值 第二行至 ...
- 洛谷P2216: [HAOI2007]理想的正方形 单调队列优化DP
洛谷P2216 )逼着自己写DP 题意: 给定一个带有数字的矩阵,找出一个大小为n*n的矩阵,这个矩阵中最大值减最小值最小. 思路: 先处理出每一行每个格子到前面n个格子中的最大值和最小值.然后对每一 ...
随机推荐
- kubernetes之DaemonSet以及滚动更新
1.什么是DaemonSet? 1.1DaemonSet是Pod控制器的又一种实现方式,用于在集群中的全部节点上同时运行一份指定的Pod资源副本,后续加入集群的节点也会自动创建一个相关的Pod对象,当 ...
- 如何有效地开发 Jmix 扩展组件
扩展组件的概念在使用 Jmix 框架开发中扮演着非常重要的角色.我们将在本文探索什么是扩展组件以及 Jmix Studio 在扩展组件开发和应用程序模块化方面能给开发者带来什么帮助. Jmix 中的扩 ...
- FHQ-Treap 简介
FHQ-treap 即非旋Treap,是一种短小精悍,功能丰富的平衡树. 据说它的效率介于 Treap 和 Splay 之间(可能是我的FHQ常数比较小,跑得比我的Treap还快). 它可以实现 Sp ...
- 编译器工程师眼中的好代码:Loop Interchange
摘要:本文将以Loop Interchange的场景为例,讲述在编写代码时可以拿到更优性能的书写方式. 本文分享自华为云社区<编译器工程师眼中的好代码(1):Loop Interchange&g ...
- (最简单详细)IronPython下载、安装及简单使用
说实话,对于我这种小白,在网上找个IronPython找的很费劲,学会操作之后,直接整个随笔,供新手参考.前提是现在你应该有VS了 (1)找到IronPython的网站 很多人肯定就按照习惯搜索,Ir ...
- 深入分析JVM执行引擎
程序和机器沟通的桥梁 一.闲聊 相信很多朋友在出国旅游,或者与外国友人沟通的过程中,都会遇到语言不通的烦恼.这时候我们就需要掌握对应的外语或者拥有一部翻译机.而笔者只会中文,所以需要借助一部翻译器才能 ...
- Hack The Box( Starting Point )
Hack The Box [Starting Point] 初始点 -- 了解渗透测试的基础知识. 这一章节对于一个渗透小白来说,可以快速的成长.以下将提供详细的解题思路,与实操步骤. TIER 0 ...
- iOS WebRTC 点对点实时音视频流程介绍
前言 公司某个项目需要接入音视频即时通讯, 功能类似微信的拨打视频通话,语音通话的场景.那么对于音视频通讯会用到什么技术呢?没错,它就是 WebRTC . 什么是WebRTC WebRTC,名称源自网 ...
- CodeForces - 1701C
Problem - C - Codeforces 题意: 每个位置对应一种适合的工人,适合的工人工作消耗1h,不适合2h,每个工人不能同时工作多个机器,问将所有机器工作完毕的最小时间是多少. 题解: ...
- 利用userfaultfd + setxattr堆占位
利用userfaultfd + setxattr堆占位 很久之前便看到过这个技术的名字,但是由于自己的摆烂,一直没有管.今天终于找到时间好好看一下这个技术的利用方式.利用userfaultfd + s ...