Luogu-3933 (二分答案)

题目

https://www.luogu.org/problemnew/show/P3933#sub
P3933 Chtholly Nota Seniorious
时空限制 1s-3s / 128MB
额,题面有点长,可以去上面链接那里看题吧,下面也会简单说一下题意。

分析

  • 题意就是给一个矩阵,要求把它分成两个区域使得每一个区域中的点都可以两两到达,且最多拐一次弯,求两区域的 极差 中间较大的一个的 最小值 是多少。(一个区域的极差指里面最大最小值得差)
  • 题意中“最多拐一次弯”,可以理解为两部分的分割线要么从上到下递增,要么递减(如图)这里写图片描述
  • 再看到又是这种较大值的最小值,可以考虑二分答案,而且也的确符合二分性质,然后问题就变成怎么判断答案是否可行。
  • 看看数据范围,时间 1~3s ,log还可以乘个 MN,于是可以这样做
    • 设二分的答案为 mid
    • 显然矩阵中最大值最小值是分开在两个区域里的
    • 然后含有最大值Max的那个区域里的所有数都不能小于 Max-mid
    • 同理含有最小值Min的那个区域里的所有数都不能大于Min+mid
    • 可以从上到下扫一遍,记下当前行两区域的区分点最右能到哪,只要这个能保证单调即可。
    • 然后由于分割线有两个方向,Min 和 Max 在两个区间的情况也能有两种,分四类(左上Min,右下Max;左上Max,右下Min;左下Min,右上Max;左下Max,右上Min),四种情况都无解代表答案不可行,否则可行。

程序

#include <cstdio>
#include <algorithm>
#define For(x,l,r) for(int x=l; x<=r; x++)
using namespace std;
int n,m,l,r,mid,ans,Max=-1,Min=1000000001,a[2005][2005];

bool check(){
    int L1=Max-mid,L2=Min+mid,P,k,i,j,F1,F2,F3,F4;
    F1=F2=F3=F4=1;
    for (i=1,j=1,P=m+1; i<=n && F1; i++,j=1){       //x1>=L1, x2<=L2
        while (a[i][j]>=L1 && j<P) j++;
        P=j;
        for (; j<=m; j++) if (a[i][j]>L2){F1=0; break;};
    }
    for (i=1,j=1,P=m+1; i<=n &&F2; i++,j=1){        //x1<=L2, x2>=L1
        while (a[i][j]<=L2 && j<P) j++;
        P=j;
        for (; j<=m; j++) if (a[i][j]<L1){F2=0; break;};
    }

    for (i=n,j=1,P=m+1; i>=1 && F3; i--,j=1){       //x1>=L1, x2<=L2
        while (a[i][j]>=L1 && j<P) j++;
        P=j;
        for (; j<=m; j++) if (a[i][j]>L2){F3=0; break;}
    }
    for (i=n,j=1,P=m+1; i>=1 && F4; i--,j=1){       //x1<=L2, x2>=L1
        while (a[i][j]<=L2 && j<P) j++;
        P=j;
        for (; j<=m; j++) if (a[i][j]<L1){F4=0; break;}
    }

    return F1+F2+F3+F4>0;
}

int main(){
    scanf("%d%d",&n,&m);
    For(i,1,n) For(j,1,m) scanf("%d",&a[i][j]),Min=min(Min,a[i][j]),Max=max(Max,a[i][j]);
    for (l=0,r=1e9,mid=l+r>>1; l<=r; mid=l+r>>1){
        if (check()){
            r=mid-1; ans=mid; continue;
        }
        l=mid+1;
    }
    printf("%d",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值