[Luogu 2216] [HAOI2007]理想的正方形

题目描述

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

输入输出格式

输入格式:

第一行为3个整数,分别表示a,b,n的值

第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。

输出格式:

仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值。

输入输出样例

输入样例#1:

  1. 5 4 2
  2. 1 2 5 6
  3. 0 17 16 0
  4. 16 17 2 1
  5. 2 10 2 1
  6. 1 2 2 2
输出样例#1:

  1. 1

说明

问题规模

(1)矩阵中的所有数都不超过1,000,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

抱着刷DP的心理,打开了这道题,但好像并不会DP qaq,这里介绍一种二维st的方法

题解:

因为本蒟蒻是刚复习了一下st表做RMQ,所以顺手继续做了

因为我们发现n是不变的,所以st表的时候可以只开三维f[a][b][log n]

然后就可以根据一维st表一样的预处理方式,只是一个状态需要从四个状态转移过来

因为一个正方形肯定是可以分成四个部分的,可能包含重叠.

所以就是这样,然后最后查询的时候也是分成四个部分

于是就结束了...

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=;
  4. int a,b,n,lg,ans=1e9;
  5. int mp[N][N],f[N][N][],g[N][N][];
  6. int Min(int a,int b,int c,int d){
  7. return min(a,min(b,min(c,d)));
  8. }
  9. int Max(int a,int b,int c,int d){
  10. return max(a,max(b,max(c,d)));
  11. }
  12. int ask1(int x,int y){
  13. int dx=x+n-,dy=y+n-;
  14. return Max(g[x][y][lg],g[x][dy-(<<lg)+][lg],g[dx-(<<lg)+][y][lg],g[dx-(<<lg)+][dy-(<<lg)+][lg]);
  15. }
  16. int ask2(int x,int y){
  17. int dx=x+n-,dy=y+n-;
  18. return Min(f[x][y][lg],f[x][dy-(<<lg)+][lg],f[dx-(<<lg)+][y][lg],f[dx-(<<lg)+][dy-(<<lg)+][lg]);
  19. }
  20. int main(){
  21. scanf("%d%d%d",&a,&b,&n); memset(f,0x3f3f,sizeof(f)); memset(g,,sizeof(g));
  22. for (int i=;i<=a;++i)
  23. for (int j=;j<=b;++j)
  24. scanf("%d",&mp[i][j]),f[i][j][]=g[i][j][]=mp[i][j];
  25. for (int k=;(<<k)<=n;++k)
  26. for (int i=;i+(<<k)-<=a;++i)
  27. for (int j=;j+(<<k)-<=b;++j){
  28. f[i][j][k]=Min(f[i][j][k-],f[i][j+(<<(k-))][k-],f[i+(<<(k-))][j][k-],f[i+(<<(k-))][j+(<<k-)][k-]);
  29. g[i][j][k]=Max(g[i][j][k-],g[i][j+(<<(k-))][k-],g[i+(<<(k-))][j][k-],g[i+(<<(k-))][j+(<<k-)][k-]);
  30. }
  31. lg=(int)(log(n)/log(2.0));
  32. for (int i=;i<=a-n+;++i)
  33. for (int j=;j<=b-n+;++j)
  34. ans=min(ans,ask1(i,j)-ask2(i,j));
  35. printf("%d",ans);
  36. }

然而事实上,我觉得单调队列的做法也是非常好的,于是借鉴了别人的题解,此下贴出


对于每一行,我们维护定长区间内的最大值和最小值,maxv[i][j]表示第i行第j列,从j-k+1~j这些数的最大值,minv[i][j]同理。这里的k是题目中的n,也就是正方形的长。然后我们已经知道每一行定长区间内的最值,对于每一列,我们也同样维护这一列定长区间的最值,就能得到一个“定正方形”内的最值。

至于定长区间的最值怎么求,那就是用到我们的单调队列了,这道题其实是个模板。这里我是开两个双端队列,maxq和minq,分别维护。(当然开一个也可以,那样代码就比较长了)

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<queue>
  4. #include<algorithm>
  5. using namespace std;
  6.  
  7. const int N = ;
  8. const int INF = 1e9;
  9. int n, m, k, a[N][N], maxv[N][N], minv[N][N];
  10.  
  11. int main()
  12. {
  13. scanf("%d%d%d", &n, &m, &k);
  14. for (int i=; i<=n; i++)
  15. for (int j=; j<=m; j++) scanf("%d", &a[i][j]);
  16. //以下对于每一行用单调队列求出maxv[i][j]和minv[i][j]
  17. for (int i=; i<=n; i++){
  18. deque<int> maxq, minq;
  19. maxv[i][] = ;
  20. minv[i][] = INF;
  21. for (int j=; j<=m; j++){
  22. while (!maxq.empty() && maxq.front() < j-k+) maxq.pop_front(); //如果范围超过k就弹出队列
  23. while (!maxq.empty() && a[i][maxq.back()] <= a[i][j]) maxq.pop_back(); //维护单调递减的队列使得队首为最大值
  24. maxq.push_back(j);
  25. maxv[i][j] = a[i][maxq.front()];
  26. while (!minq.empty() && minq.front() < j-k+) minq.pop_front();
  27. while (!minq.empty() && a[i][minq.back()] >= a[i][j]) minq.pop_back(); //维护单调递增的队列使得队首为最小值
  28. minq.push_back(j);
  29. minv[i][j] = a[i][minq.front()];
  30. }
  31. }
  32. //以下对于每一列用单调队列求出“定正方形”内最值,并直接计算答案
  33. int ans = INF;
  34. for (int j=k; j<=m; j++){ //注意枚举范围从k开始
  35. deque<int> maxq, minq;
  36. int MaxV = ;
  37. int MinV = INF;
  38. for (int i=; i<=n; i++){
  39. //单调队列用法同上
  40. while (!maxq.empty() && maxq.front() < i-k+) maxq.pop_front();
  41. while (!maxq.empty() && maxv[maxq.back()][j] <= maxv[i][j]) maxq.pop_back();
  42. maxq.push_back(i);
  43. MaxV = maxv[maxq.front()][j];
  44. while (!minq.empty() && minq.front() < i-k+) minq.pop_front();
  45. while (!minq.empty() && minv[minq.back()][j] >= minv[i][j]) minq.pop_back();
  46. minq.push_back(i);
  47. MinV = minv[minq.front()][j];
  48. if (i >= k) ans = min(ans, MaxV - MinV); //注意i >= k时才能更新答案
  49. }
  50. }
  51. printf("%d\n", ans);
  52. return ;
  53. }

[Luogu 2216] [HAOI2007]理想的正方形的更多相关文章

  1. Luogu 2216[HAOI2007]理想的正方形 - 单调队列

    Solution 二维单调队列, 这个数组套起来看得我眼瞎... Code #include<cstdio> #include<algorithm> #include<c ...

  2. Luogu 2216 [HAOI2007]理想的正方形 (单调队列优化)

    题意: 给出一个 N×M 的矩阵,以及一个数值 K ,求在给定的矩阵中取出一个 K×K 的矩阵其中最大值减去最小值的最小值. 细节: 没有细节来发暴力走天下,20分也是分啊~~~ QAQ. 分析: 感 ...

  3. BZOJ1047或洛谷2216 [HAOI2007]理想的正方形

    BZOJ原题链接 洛谷原题链接 显然可以用数据结构或\(ST\)表或单调队列来维护最值. 这里采用单调队列来维护. 先用单调队列维护每一行的最大值和最小值,区间长为正方形长度. 再用单调队列维护之前维 ...

  4. 洛谷 2216 [HAOI2007]理想的正方形

    题目戳这里 一句话题意 给你一个a×b的矩形,求一个n×n的子矩阵,矩阵里面的最大值和最小值之差最小. Solution 这个题目许多大佬都是单调队列,但是我不是很会,只好用了比较傻逼的方法: 首先我 ...

  5. BZOJ1047: [HAOI2007]理想的正方形 [单调队列]

    1047: [HAOI2007]理想的正方形 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2857  Solved: 1560[Submit][St ...

  6. HAOI2007 理想的正方形

    1047: [HAOI2007]理想的正方形 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1402  Solved: 738[Submit][Sta ...

  7. RAM——[HAOI2007]理想的正方形

    题目:[HAOI2007]理想的正方形 描述: [问题描述] 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. [输入]: 第一行为3个 ...

  8. bzoj 1047 : [HAOI2007]理想的正方形 单调队列dp

    题目链接 1047: [HAOI2007]理想的正方形 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2369  Solved: 1266[Submi ...

  9. BZOJ 1047: [HAOI2007]理想的正方形( 单调队列 )

    单调队列..先对每一行扫一次维护以每个点(x, y)为结尾的长度为n的最大最小值.然后再对每一列扫一次, 在之前的基础上维护(x, y)为结尾的长度为n的最大最小值. 时间复杂度O(ab) (话说还是 ...

随机推荐

  1. [API 开发管理] 分享几个 eoLinker 实用操作技巧

    一键离线导出项目,PDF.WORD等格式任你挑选 举例说明,如果我要将 "示例素材项目" 导出到本地,并且以 PDF 的格式保存. 首先找到该项目所在空间:演示空间,在左边一级菜单 ...

  2. idea热更新配置

    idea部署热启动如下,经过本人实验 在这里只能选择exploded因为它支持热部署 在这里选择如下 到这里已经完成热部署了,如果有问题欢迎反馈给我,我会及时回复

  3. BZOJ 1602 USACO 2008 Oct. 牧场行走

    [题解] 要求出树上两点间的距离,树上的边有边权,本来应该是个LCA. 看他数据小,Xjb水过去了...其实也算是LCA吧,一个O(n)的LCA... #include<cstdio> # ...

  4. [bzoj3676]回文串[后缀数组+Manacher]

    后缀数组+Manacher #include <iostream> #include <cstdio> #include <cstdlib> #include &l ...

  5. 利用C语言中的函数指针实现c++中的虚函数

    C语言中的函数指针 #include<stdio.h> int fun1(int a) { return a*a; } int fun2(int a) { return a*a*a; } ...

  6. F1: A Distributed SQL Database That Scales GOOGLE F1 论文

    http://research.google.com/pubs/pub41344.html http://research.google.com/pubs/pub36726.html

  7. SSH整合开发时Scope为默认时现象与原理

    1.前提知识 1)scope默认值 进行SSH整合开发时,Struts2的action须要用spring容器进行管理,仅仅要涉及到类以bean的形式入到spring容器中.无论是xml配置还是使用注解 ...

  8. codeforces 391E2 (【Codeforces Rockethon 2014】E2)

    题目:http://codeforces.com/problemset/problem/391/E2    题意:有三棵树.每棵树有ni个结点,加入两条边把这三棵树连接起来,合并成一棵树.使得合并的树 ...

  9. 【HDU 4870】Rating【DP】

    题意:一个人注冊两个账号,初始rating都是0,他每次拿低分的那个号去打比赛,赢了加50分,输了扣100分.胜率为p,他会打到直到一个号有1000分为止,问比赛场次的期望. 题解:因为每次添加分数或 ...

  10. jsp模板配置

    <%-- Created by IntelliJ IDEA. User: ${USER} Date: ${DATE} Time: ${TIME} To change this template ...