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

  • 相关阅读:
    461. Hamming Distance
    342. Power of Four
    326. Power of Three
    368. Largest Divisible Subset java solutions
    95. Unique Binary Search Trees II java solutions
    303. Range Sum Query
    160. Intersection of Two Linked Lists java solutions
    88. Merge Sorted Array java solutions
    67. Add Binary java solutions
    14. Longest Common Prefix java solutions
  • 原文地址:https://www.cnblogs.com/logic-yzf/p/7650028.html
Copyright © 2011-2022 走看看