zoukankan      html  css  js  c++  java
  • [Luogu 2216] [HAOI2007]理想的正方形

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

    题目描述

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

    输入输出格式

    输入格式:

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

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

    输出格式:

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

    输入输出样例

    输入样例#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)矩阵中的所有数都不超过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=1010;
     4 int a,b,n,lg,ans=1e9;
     5 int mp[N][N],f[N][N][11],g[N][N][11];
     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-1,dy=y+n-1;
    14     return Max(g[x][y][lg],g[x][dy-(1<<lg)+1][lg],g[dx-(1<<lg)+1][y][lg],g[dx-(1<<lg)+1][dy-(1<<lg)+1][lg]);
    15 }
    16 int ask2(int x,int y){
    17     int dx=x+n-1,dy=y+n-1;
    18     return Min(f[x][y][lg],f[x][dy-(1<<lg)+1][lg],f[dx-(1<<lg)+1][y][lg],f[dx-(1<<lg)+1][dy-(1<<lg)+1][lg]);
    19 }
    20 int main(){
    21     scanf("%d%d%d",&a,&b,&n); memset(f,0x3f3f,sizeof(f)); memset(g,0,sizeof(g));
    22     for (int i=1;i<=a;++i)
    23         for (int j=1;j<=b;++j)
    24             scanf("%d",&mp[i][j]),f[i][j][0]=g[i][j][0]=mp[i][j];
    25     for (int k=1;(1<<k)<=n;++k)
    26         for (int i=1;i+(1<<k)-1<=a;++i)
    27             for (int j=1;j+(1<<k)-1<=b;++j){
    28                 f[i][j][k]=Min(f[i][j][k-1],f[i][j+(1<<(k-1))][k-1],f[i+(1<<(k-1))][j][k-1],f[i+(1<<(k-1))][j+(1<<k-1)][k-1]);
    29                 g[i][j][k]=Max(g[i][j][k-1],g[i][j+(1<<(k-1))][k-1],g[i+(1<<(k-1))][j][k-1],g[i+(1<<(k-1))][j+(1<<k-1)][k-1]);
    30             }
    31     lg=(int)(log(n)/log(2.0));
    32     for (int i=1;i<=a-n+1;++i)
    33         for (int j=1;j<=b-n+1;++j)
    34             ans=min(ans,ask1(i,j)-ask2(i,j));
    35     printf("%d",ans);
    36 }
    View Code

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


    对于每一行,我们维护定长区间内的最大值和最小值,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 = 1010;
     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=1; i<=n; i++)
    15         for (int j=1; j<=m; j++) scanf("%d", &a[i][j]);
    16     //以下对于每一行用单调队列求出maxv[i][j]和minv[i][j]
    17     for (int i=1; i<=n; i++){
    18         deque<int> maxq, minq;
    19         maxv[i][0] = 0;
    20         minv[i][0] = INF;
    21         for (int j=1; j<=m; j++){
    22             while (!maxq.empty() && maxq.front() < j-k+1) 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+1) 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 = 0;
    37         int MinV = INF;
    38         for (int i=1; i<=n; i++){
    39             //单调队列用法同上
    40             while (!maxq.empty() && maxq.front() < i-k+1) 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+1) 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
    ", ans);
    52     return 0;
    53 }
    View Code

  • 相关阅读:
    HDFS文件系统
    使用cmake安装单实例mysql
    python读取数据库表数据并写入excel
    linux文件行数统计
    ORACLE清空回收站操作
    Oracle 11g静默安装
    图解Java设计模式之职责链模式
    图解Java设计模式之策略模式
    图解Java设计模式之状态模式
    图解Java设计模式之解释器模式
  • 原文地址:https://www.cnblogs.com/logic-yzf/p/7650028.html
Copyright © 2011-2022 走看看