• NOIP模拟——矩阵分组


    有N行M列的矩阵,每个格子中有一个数字,现在需要你将格子的数字分为A,B两部分

    要求:

    1、每个数字恰好属于两部分的其中一个部分

    2、每个部分内部方块之间,可以上下左右相互到达,且每个内部方块之间可以相互到达,且最多拐一次弯

    如:

    AAAAA  AAAAA  AAAAA
    AABAA  BaAAA  AAABB
    ABBBA  BBAAA  AAABB
    AABAA  BaAAA  ABBBB
    AAAAA  AAAAA  BBBBB
    
      (1)     (2)     (3) 

    其中(1)(2)是不允许的分法,(3)是允许的分法。在(2)中,a属于A区域,这两个a元素之间互相到达,但是不满足只拐一次弯到达。

    问:对于所有合法的分组中,A区域和B区域的极差,其中极差较大的一个区域最小值是多少

    提示:极差就是区域内最大值减去最小值。

    输入

    第一行两个正整数n,m

    接下来n 行,每行m个自然数A_{i,j}表示权值

    输出

    输出一行表示答案

    样例输入
    4 4
    1 12 6 11
    11 4 2 14
    10 1 9 20
    4 17 13 10
    样例输出
    11
    提示

    【样例解释】 1 12 6 11 11 4 2 14 10 1 9 20 4 17 13 10

    分法不唯一,如图是一种合法的分法。左边部分极差12-1=11,右边一块极差20-10=10,所以答案取这两个中较大者11。没有别的分法,可以使答案更小。

    测试点N,m范围
    1,2 n<=10,m<=10
    3-4 n=1,m<=2000
    5-7 n<=200,m<=200
    8-10 n<=2000,m<=2000

    所有权值1<=a_ij<=10^9

    肯定是二分答案了,枚举最大的极差,带入到最大值所在的区域内,区域分好后再判断是否满足

    可以证明分好的区域边缘必然是一个楼梯样的,高度递减的(我觉得不用证明吧)

    而且每个区域必定会占一个角,

    而且最大和最小的肯定不能在同一个区域,否则还玩啥啊

    如果我们从最大值所在的区域来看的话

    可能会分别在不同的角上

    而在不同的角判断的方法不一样

    因为写好几种判断太麻烦了

    所以直接存把矩阵旋转90.180.270度的情况一起存下来

    相当于默认角在某一个位置,这样就可以用一种判断的方法就可以把所有情况都判断完了

    不过存储的写法很是巧妙啊

    看代码吧

    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
        char ch=getchar();
        int res=0;
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
        return res;
    }
    int n,m,a[4][2005][2005],gmax=-2e9-1,gmin=2e9+1,endi[2005];
    inline bool check(int u,int k)
    {
        if(u&1) swap(m,n);
        endi[0]=m;
        for(int i=1,j;i<=n;i++)
        {
            for(j=1;j<=endi[i-1];j++)
            {
                if(gmax>a[u][i][j]+k)
                break;
            }
            endi[i]=j-1;
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=endi[i]+1;j<=m;j++)
            {
                if(a[u][i][j]>gmin+k)
                {
                    if(u&1) swap(m,n);
                    return false;
                }
            }
        }
        if(u&1) swap(m,n);
        return true;
    }
    inline bool che(int k)
    {
        for(int i=0;i<4;i++)
        {
            if(check(i,k)) return true;
        }
        return false;
    }
    int main(){
        n=read(),m=read();
        int x=1,x1=1,x2=n,x3=m,y=1,y1=n,y2=m,y3=1,t;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                t=a[0][x][y++]=a[1][x1++][y1]=a[2][x2][y2--]=a[3][x3--][y3]=read();
                if(t>gmax)gmax=t;
                if(t<gmin) gmin=t;
            }
            x++,y=1;
            y1--,x1=1;
            x2--,y2=m;
            y3++,x3=n;
        }
        int l=0,r=gmax-gmin;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(che(mid)) r=mid;
            else l=mid+1;
        }
        cout<<l<<endl;
        return 0;
    }
  • 相关阅读:
    [Spring开发]获取上下文对象
    [Dubbo开发]Dubbo日志插件实现(打包)
    [Dubbo开发]Dubbo日志插件实现(未打包)
    [Java开发]打印当前路径到控制台
    [Dubbo开发]Dubbo拦截器(Filter)初探
    [Dubbo开发]配置简单的生产者和消费者
    [Dubbo开发]Zookeeper配置
    [Dubbo开发]Maven安装与配置
    EL表达式的特性
    oracle中rownum的使用
  • 原文地址:https://www.cnblogs.com/forever-/p/9737430.html
Copyright © 2020-2023  润新知