[HAOI2007]理想的正方形 st表 || 单调队列
题解:
因为数据范围不大,而且题目要求的是正方形,所以这道题有2种解法。
1,st表。
这种解法暴力好写好理解,但是较慢。我们设st[i][j][k]表示以(i, j)为左端点,向下/向右分别扩展$2^k$格的最大值,最小值同理,处理完后$n^2$枚举左端点取最优值即可。
(此为早期代码,写丑了不要介意)
- #include<bits/stdc++.h>
- using namespace std;
- #define R register int
- #define AC 1010
- #define ac 110
- //#define getchar() *S ++
- //char READ[1250000],*S = READ;
- int n,a,b,ans = INT_MAX;
- int st_max[AC][AC][], st_min[AC][AC][];
- int k, q = ;
- //二维ST表emmmm
- inline int read()
- {
- int x = ;char c = getchar();
- while(c > '' || c < '') c = getchar();
- while(c >= '' && c <= '') x = x * + c - '', c = getchar();
- return x;
- }
- inline int Max(int a, int b, int c, int d)
- {
- if(a > b && a > c && a > d) return a;
- else if(b > c && b > d) return b;
- else if(c > d) return c;
- else return d;
- }
- inline int Min(int a, int b, int c, int d)
- {
- if(a < b && a < c && a < d) return a;
- else if(b < c && b < d) return b;
- else if(c < d) return c;
- else return d;
- }
- void pre()
- {
- a = read(), b = read(), n = read();
- for(R i = ; i <= a; i ++)
- for(R j = ; j <= b; j ++)
- st_max[i][j][] = st_min[i][j][] = read();
- }
- void check()
- {
- for(R i = ; i <= a; i ++)
- for(R j = ; j <= b; j ++)
- {
- printf("!!!(%d , %d)\nst_max:\n", i, j);
- for(R l = ; l <= k; l ++)
- printf("2^%d = %d\n", l, st_max[i][j][l]);
- printf("\n");
- printf("st_min:\n");
- for(R l = ; l <= k; l ++)
- printf("2^%d = %d\n", l, st_min[i][j][l]);
- printf("\n\n");
- }
- }
- void build()
- {
- while(n > q) q <<= , ++ k;
- -- k, q >>= ;
- int pos=;
- for(R l = ; l <= k; l ++)
- {
- for(R i = pos + ; i <= a; i ++)
- {
- for(R j = pos + ; j <= b; j ++)
- {
- st_max[i][j][l] = Max(st_max[i - pos][j][l - ], st_max[i][j - pos][l - ], st_max[i - pos][j - pos][l - ], st_max[i][j][l - ]);
- st_min[i][j][l] = Min(st_min[i - pos][j][l - ], st_min[i][j - pos][l - ], st_min[i - pos][j - pos][l - ], st_min[i][j][l - ]);
- }
- }
- pos <<= ;
- }
- }
- void work()
- {
- int maxn, minn;
- for(R i = n; i <= a; i ++)
- for(R j = n; j <= b; j ++)
- {
- maxn = Max(st_max[i][j][k], st_max[i - n + q][j - n + q][k], st_max[i - n + q][j][k], st_max[i][j - n + q][k]);
- minn = Min(st_min[i][j][k], st_min[i - n + q][j - n + q][k], st_min[i - n + q][j][k], st_min[i][j - n + q][k]);
- ans = min(ans, maxn - minn);
- }
- printf("%d\n", ans);
- }
- int main()
- {
- // freopen("in.in", "r", stdin);
- //fread(READ, 1, 1200000, stdin);
- pre();
- build();
- //check();
- work();
- // fclose(stdin);
- return ;
- }
2,单调队列。
其实也好理解,,,但是感觉很多博客没有图所以意思讲的不是很清晰,这里就详细讲一下吧。
类似于滑动窗口,如果没做过这题建议先理解这题的做法。
可以看做此题就是滑动窗口的二维扩展版。那么我们已经有了在序列上获取指定区间大小的最大最小值的方法,要如何才能扩展到二维平面上呢?
其实画个图就很好理解了。
如果我们将每个红色区间的最大最小值都存在蓝色点上,那么只需要对蓝色点做一次滑动窗口,就可以获得指定大小的矩形最大最小值了。
因为每个蓝色点已经代表了指定区间大小的行的最大最小值,所以再在这个基础上查询蓝点指定区间的最大最小值就相当于是在查询一个矩形了。
- #include<bits/stdc++.h>
- using namespace std;
- #define R register int
- #define AC 1100
- #define LL long long
- int n, m, k, ans = INT_MAX;
- int s[AC][AC], g[AC][AC], f[AC][AC];
- struct node{
- int x, id;
- };
- struct que{
- node q[AC];int head, tail;
- void init()
- {
- head = , tail = ;
- }
- void add_max(int x, int id)
- {
- while(head <= tail && q[head].id <= id - k) ++ head;
- while(head <= tail && q[tail].x <= x) -- tail;
- q[++tail] = (node){x, id};
- }
- void add_min(int x, int id)
- {
- while(head <= tail && q[head].id <= id - k) ++ head;
- while(head <= tail && q[tail].x >= x) -- tail;
- q[++tail] = (node){x, id};
- }
- int top() {return q[head].x;}
- }q1, q2;
- inline void upmin(int &a, int b)
- {
- if(b < a) a = b;
- }
- inline int read()
- {
- int x = ;char c = getchar();
- while(c > '' || c < '') c = getchar();
- while(c >= '' && c <= '') x = x * + c - '', c = getchar();
- return x;
- }
- void pre()
- {
- n = read(), m = read(), k = read();
- for(R i = ; i <= n; i ++)
- for(R j = ; j <= m; j ++) s[i][j] = read();
- }
- void build()//先对每一行求出来
- {
- for(R i = ; i <= n; i ++)//枚举行
- {
- q1.init(), q2.init();
- for(R j = ; j <= m; j ++)//枚举列
- {
- q1.add_min(s[i][j], j), q2.add_max(s[i][j], j);
- if(j >= k) f[i][j] = q1.top(), g[i][j] = q2.top();
- }
- }
- }
- void work()//再求整体的
- {
- for(R i = k; i <= m; i ++)//先枚举列,再枚举行
- {
- q1.init(), q2.init();
- for(R j = ; j <= n; j ++)
- {
- q1.add_min(f[j][i], j), q2.add_max(g[j][i], j);
- if(j >= k) upmin(ans, q2.top() - q1.top());
- }
- }
- printf("%d\n", ans);
- }
- int main()
- {
- // freopen("in.in", "r", stdin);
- pre();
- build();
- work();
- // fclose(stdin);
- return ;
- }
[HAOI2007]理想的正方形 st表 || 单调队列的更多相关文章
- P2216 [HAOI2007]理想的正方形(dp+单调队列优化)
题目链接:传送门 题目: 题目描述 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. 输入输出格式 输入格式: 第一行为3个整数,分别表 ...
- [BZOJ1047][HAOI2007]理想的正方形 二维单调队列
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1047 我们对每矩阵的一列维护一个大小为$n$的单调队列,队中元素为矩阵中元素.然后扫描每一 ...
- bzoj1047 [HAOI2007]理想的正方形——二维单调队列
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1047 就是先对行做一遍单调队列,再对那个结果按列做一遍单调队列即可. 代码如下: #incl ...
- [Bzoj1047][HAOI2007]理想的正方形(ST表)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1047 题目虽然有一个n的限制,但求二维区间最值首先想到的还是RMQ,但是如果按照往常RM ...
- Codeforces Round #278 (Div. 1) B - Strip dp+st表+单调队列
B - Strip 思路:简单dp,用st表+单调队列维护一下. #include<bits/stdc++.h> #define LL long long #define fi first ...
- [luogu2216 HAOI2007] 理想的正方形 (2dST表 or 单调队列)
题目描述 有一个ab的整数组成的矩阵,现请你从中找出一个nn的正方形区域,使得该区域所有数中的最大值和最小值的差最小. 输入输出格式 输入格式: 第一行为3个整数,分别表示a,b,n的值 第二行至第a ...
- [Bzoj4540][Hnoi2016] 序列(莫队 + ST表 + 单调队列)
4540: [Hnoi2016]序列 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1567 Solved: 718[Submit][Status] ...
- P2216 [HAOI2007]理想的正方形
题目描述 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. 输入输出格式 输入格式: 第一行为3个整数,分别表示a,b,n的值 第二行至 ...
- BZOJ1047: [HAOI2007]理想的正方形 [单调队列]
1047: [HAOI2007]理想的正方形 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2857 Solved: 1560[Submit][St ...
随机推荐
- I/O流、字符集
1)InputStream.OutPutStream是输出输入的基础流,均为抽象类,提供了read和writer方法,所有的子类均实现read和writer方法,read在遇到输入源的结尾时返回-1. ...
- 响应式布局--设置rem自适应
//designWidth:设计稿的实际宽度值,需要根据实际设置 //maxWidth:制作稿的最大宽度值,需要根据实际设置 //这段js的最后面有两个参数记得要设置,一个为设计稿实际宽度,一个为制作 ...
- 【Hive二】 Hive基本使用
Hive基本使用 创建数据库 创建一个数据库,数据库在HDFS上的默认存储路径是/user/hive/warehouse/*.db create database 库名; 避免要创建的数据库已经存在错 ...
- ctf题目writeup(2)
2019.1.29 题目地址: https://www.ichunqiu.com/battalion 1. 点开链接: include "flag.php";$a = @$_REQ ...
- 第一天的题目 简单A+B 植树问题 多项式的值
#include<stdio.h> int main() { int a=0;b=0; scanf("%d%d",&a,&b); printf(&quo ...
- ScriptManager和UpdatePanel用法 (ajax)
ScriptManager和UpdatePanel控件联合使用可以实现页面异步局部更新的效果.其中的UpdatePanel就是设置页面中异 步局部更新区域,它必须依赖于ScriptManager存在, ...
- (数据科学学习手札08)系统聚类法的Python源码实现(与Python,R自带方法进行比较)
聚类分析是数据挖掘方法中应用非常广泛的一项,而聚类分析根据其大体方法的不同又分为系统聚类和快速聚类,其中系统聚类的优点是可以很直观的得到聚类数不同时具体类中包括了哪些样本,而Python和R中都有直接 ...
- ArrayList底层原理
ArrayList底层采用数组实现,访问特别快,它可以根据索引下标快速找到元素.但添加插入删除等写操作效率低,因为涉及到内存数据复制转移. ArrayList对象初始化时,无参数构造器默认容量为10, ...
- 不同编译器下,定义一个地址按x字节对齐的数组
以前一直用MDK,用__align(4)就可以定义一个首地址被4整除.地址按4字节对齐的数组,但今天用IAR发现这么写编译报错. 搜了一下才发现,原来不同的编译器,需要用不同的表达方式: #if de ...
- mysql ON DUPLICATE KEY UPDATE、REPLACE INTO
INSERT INTO ON DUPLICATE KEY UPDATE 与 REPLACE INTO,两个命令可以处理重复键值问题,在实际上它之间有什么区别呢?前提条件是这个表必须有一个唯一索引或主键 ...