• [HAOI2007]理想的正方形


    题目

    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)//如果队列长度大于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)//如果队列长度大于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]];
                //k 代表第几横排 ,tot 是第几个 n 宽度的min 
            }
        }
        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)//如果队列长度大于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)//如果队列长度大于n 那么踢队头 
                    head++;
                while(head<=tail&&val[i]<val[q[tail]])
                    tail--;
                q[++tail]=i;
                if(i>=n)
                    sum[t][++ss]=val[q[head]];
                //t 代表第几横排 ,s 是竖排第几个 n 宽度的min 
            }
        }
        ll answer=1<<30;
        for(re ll i=1;i<=tot;i++)
        for(re ll j=1;j<=s;j++)//枚举每个横排长度为 n 的 个数 和竖排的长度为 n 的个数  
            answer=min(answer,ans[i][j]-sum[i][j]);//每个n*n区域的max与min 之差 求最小值 
        printf("%lld
    ",answer);
    }
  • 相关阅读:
    万里长征,始于足下——菜鸟程序员的学习总结(二)
    google云笔记keep试用感受
    程序员英语学习—理论篇(一)
    LLVM每日谈之十六 LLVM的学习感悟
    LLVM每日谈之十五 LLVM自带的examples
    C++学习书单
    人品计算器小案例
    隐式意图&显示意图
    Activity有多个启动图标
    Android中四大组件
  • 原文地址:https://www.cnblogs.com/wzx-RS-STHN/p/13419103.html
Copyright © 2020-2023  润新知