首页 > 其他 > 详细

[HAOI2007]理想的正方形

时间:2020-08-02 23:49:02      阅读:102      评论:0      收藏:0      [点我收藏+]

题目

Description

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

Input

第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。
100%的数据2<=a,b<=1000,n<=a,n<=b,n<=1000

Output

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

Sample Input

5 4 2
1  2  5  6
0  17 16 0
16 17 2  1
2  10 2  1
1  2  2  2

Sample Output

1

思路

根据题目样例的意思;

1     2    5    6

0    17  16   0

16  17   2    1

2    10   2    1

1     2    2    2

我们可以求出每横排 n 个宽度的最大值;

也就是这样

 2      5     6

17    17   16

17    17    2

10    10    2

 2      2     2

然后再求竖排 n 个宽度的最大值;

也就是

17    17    16

17    17    16

17    17     2

10    10     2

这样每个点就代表一个n*n 的正方形;

 

然后用同样的方法求一波每个正方形代表的最小值;

最后,根据题意求出

n*n正方形区域中的最大整数和最小整数的差值 的最小值

也就是把上面每个点的值相减,然后求一波 min 就ok了;

 

代码

#include<bits/stdc++.h>
#define re register
typedef long long ll;
using namespace std;
inline ll read()
{
    ll a=0,f=1; char c=getchar();
    while (c<0||c>9) {if (c==-) f=-1; c=getchar();}
    while (c>=0&&c<=9) {a=a*10+c-0; c=getchar();}
    return a*f; 
}
ll a,b,n;
ll map1[1010][1010];
ll val[1010];
ll mx[1010][1010],mn[1010][1010];
ll ans[1010][1010],sum[1010][1010];
ll q[1010];
int main()
{
    a=read(); b=read(); n=read();
    for(re ll i=1;i<=a;i++)
    for(re ll j=1;j<=b;j++)
        map1[i][j]=read();//读入 
    ll tot=0;
    for(re ll k=1;k<=a;k++)//k 代表每一行 
    {
        ll head=1,tail=0;
        tot=0;
        for(re ll i=1;i<=b;i++)// i 是每一列 
        {
            while(head<=tail&&i-q[head]+1>n)
                head++;
            while(head<=tail&&map1[k][i]>map1[k][q[tail]])
                tail--;
            q[++tail]=i;
            if(i>=n)
                mx[k][++tot]=map1[k][q[head]];//单调队列板子,横排求一波最大值; 
            //k 代表第几横排 ,tot 是第几个 n 宽度的max 
        }
    }
    //同上,横排求一波最小值; 
    ll cnt=0;
    for(re ll k=1;k<=a;k++)
    {
        ll head=1,tail=0;
        cnt=0;
        for(re ll i=1;i<=b;i++)
        {
            while(head<=tail&&i-q[head]+1>n)
                head++;
            while(head<=tail&&map1[k][i]<map1[k][q[tail]])
                tail--;
            q[++tail]=i;
            if(i>=n)
                mn[k][++cnt]=map1[k][q[head]];
        }
    }
    ll s=0;
    for(re ll t=1;t<=tot;t++)//横排的max 个数 
    {
        s=0;
        for(re ll k=1;k<=a;k++)
            val[k]=mx[k][t];//将每一竖排的之前求出的max 值记下; 
        ll head=1,tail=0;
        for(re ll i=1;i<=a;i++)
        {
            while(head<=tail&&i-q[head]+1>n)
                head++;
            while(head<=tail&&val[i]>val[q[tail]])//单调队列板子,竖排求一波最大值; 
                tail--;
            q[++tail]=i;
            if(i>=n)
                ans[t][++s]=val[q[head]];
            //t 代表第几横排 ,s 是竖排第几个 n 宽度的max
        }
    }
    memset(sum,127/3,sizeof(sum));
    ll ss=0;
    //同上 ,求一波最小值; 
    for(re ll t=1;t<=cnt;t++)
    {
        ss=0;
        for(re ll k=1;k<=a;k++)
            val[k]=mn[k][t];
        ll head=1,tail=0;
        for(re ll i=1;i<=a;i++)
        {
            while(head<=tail&&i-q[head]+1>n)
                head++;
            while(head<=tail&&val[i]<val[q[tail]])
                tail--;
            q[++tail]=i;
            if(i>=n)
                sum[t][++ss]=val[q[head]];
        }
    }
    ll answer=1<<30;
    for(re ll i=1;i<=tot;i++)
    for(re ll j=1;j<=s;j++)
        answer=min(answer,ans[i][j]-sum[i][j]);//每个n*n区域的max与min 之差 求最小值 
    printf("%lld\n",answer);
}

 

5 4 2
1  2  5  6
0  17 16 0
16 17 2  1
2  10 2  1
1  2  2  2

[HAOI2007]理想的正方形

原文:https://www.cnblogs.com/wzx-RS-STHN/p/13419103.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!