汽车拉力比赛(并查集+二分)

汽车拉力比赛

题目描述

博艾市将要举行一场汽车拉力比赛。

赛场凹凸不平,所以被描述为 N ∗ M N*M NM 的网格来表示海拔高度 ( 1 ≤ M , N ≤ 500 ) (1 \leq M,N \leq 500) (1M,N500),每个单元格的海拔范围在 0 0 0 1 0 9 10^9 109 之间。

其中一些单元格被定义为路标。组织者希望给整个路线指定一个难度系数 D D D,这样参赛选手从任一路标到达别的路标所经过的路径上相邻单元格的海拔高度差不会大于 D D D 。也就是说这个难度系数 D D D 指的是保证所有路标相互可达的最小值。任一单元格和其东西南北四个方向上的单元格都是相邻的。

输入格式

1 1 1 行两个整数 M M M N N N。第 2 2 2 行到第 M + 1 M+1 M+1 行,每行 N N N 个整数描述海拔高度。第 2 + M 2+M 2+M 行到第 1 + 2 M 1+2M 1+2M

行,每行 N N N 个整数,每个数非 0 0 0 1 1 1 1 1 1 表示该单元格是一个路标。

输出格式

一个整数,即赛道的难度系数 D D D

样例 #1

样例输入 #1

3 5 
20 21 18 99 5  
19 22 20 16 26
18 17 40 60 80
1 0 0 0 1
0 0 0 0 0
0 0 0 0 1

样例输出 #1

21

思路

  • 首先,难度值最大值最小——用二分。
  • 其次,其次,题目说相邻的高度差不大于D,而且有规定从路标到路标,因此这里就是联通性问题——并查集。
  • 最后检查每个路标是否联通。

代码

//首先,难度值最大值最小——用二分
//其次,题目说相邻的高度差不大于D,而且有规定从路标到路标,因此这里就是联通性问题——并查集

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>

#define x first
#define y second
#define int long long

using namespace std;

typedef pair<int,int>PII;

const int N = 510;

int p[N*N];
int w[N][N];
int n,m;
PII e[N*N];
int cnt;
bool st[N][N];
int mark[N][N],idx;//类似拯救大兵瑞恩的那道题思路
int dx[]={0,1,0,-1},dy[]={1,0,-1,0};

int find(int x){
    if(x!=p[x])p[x]=find(p[x]);
    return p[x];
}

bool check(int mid){
    
    for(int i=1;i<=n*m;i++)p[i]=i;
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            for(int k=0;k<4;k++){
                int a=i+dx[k],b=j+dy[k];
                if(a<1||b<1||a>n||b>m)continue;
                if(abs(w[i][j]-w[a][b])>mid)continue;
                p[find(mark[i][j])]=find(mark[a][b]);
            }
        }
    }
    
    for(int i=1;i<cnt;i++){
        if(find(mark[e[i].x][e[i].y])!=find(mark[e[i-1].x][e[i-1].y])){
            return false;
        }
    }
    return true;
}

signed main(){
    cin>>n>>m;
    
    int r=0;
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            mark[i][j]=++idx;
        }
    }
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>w[i][j];
            r=max(r,w[i][j]);
        }
    }
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            int x;
            cin>>x;
            if(x){
                e[cnt++]={i,j};
            }
        }
    }
    
    int l=-1;
    r++;
    
    while(l+1!=r){
        int mid=(l+r)>>1;
        if(check(mid))r=mid;
        else l=mid;
    }
    cout<<r;
    
    return 0;
}
  • 25
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值