Acwing5407管道


theme: fancy

题目

有一根长度为 len 的横向的管道,该管道按照单位长度分为 len 段,每一段的中央有一个可开关的阀门和一个检测水流的传感器。

一开始管道是空的,位于 Li 的阀门会在 Si 时刻打开,并不断让水流入管道。

对于位于 Li 的阀门,它流入的水在 Ti(Ti≥Si)时刻会使得从第 Li−(Ti−Si) 段到第 Li+(Ti−Si)段的传感器检测到水流。

求管道中每一段中间的传感器都检测到有水流的最早时间。

输入格式

输入的第一行包含两个整数 n,len用一个空格分隔,分别表示会打开的阀门数和管道长度。

接下来 n 行每行包含两个整数 Li,Si,用一个空格分隔,表示位于第 Li 段管道中央的阀门会在 Si 时刻打开。

输出格式

输出一行包含一个整数表示答案。

数据范围

对于 30%的评测用例,n≤200,Si,len≤3000

对于 70% 的评测用例,n≤5000,Si,len≤105

对于所有评测用例,1≤n≤105,1≤Si,len≤109,1≤Li≤len1,Li−1<Li

输入样例:
3 10
1 1
6 5
10 2
输出样例:
5

代码与思路

import java.util.*;

public class Main {
    static class Pair implements Comparable<Pair> {
        int x, y;

        public Pair(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public int compareTo(Pair o) {
            return Integer.compare(this.x, o.x);
        }
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();  // 事件数量
        int m = in.nextInt();  // 区间右端点

        Pair[] w = new Pair[n];  // 事件数组
        for (int i = 0; i < n; i++) {
            int L = in.nextInt();  // 事件左端点
            int S = in.nextInt();  // 事件发生时间
            w[i] = new Pair(L, S);  // 构建事件 Pair 对象并加入数组
        }

        // 调用二分查找方法,找到满足条件的最小时间点
        int ans = binarySearch(w, m);
        System.out.println(ans);
    }

    // 检查是否存在一个时间点 mid,使得所有事件在该时间点前结束
    static boolean check(Pair[] w, int m, int mid) {
        List<Pair> q = new ArrayList<>();  // 存储事件在时间点 mid 时的区间范围
        for (int i = 0; i < w.length; i++) {
            int L = w[i].x;  // 事件 i 的左端点位置
            int S = w[i].y;  // 事件 i 的发生时间
            if (S <= mid) {  // 如果事件 i 在当前时间 mid 之前结束
                int t = mid - S;  // 计算事件 i 的持续时间
                int l = Math.max(1, L - t);  // 计算事件 i 在当前时间 mid 的左端点位置
                int r = Math.min(m, L + t);  // 计算事件 i 在当前时间 mid 的右端点位置
                q.add(new Pair(l, r));  // 将事件 i 的区间范围加入到列表中
            }
        }
        Collections.sort(q);  // 将事件按左端点位置进行排序

        int st = -1, ed = -1;  // 初始化起始和结束端点
        // 遍历事件列表,合并区间并更新起始和结束端点
        for (Pair p : q) {
            if (p.x <= ed + 1) {
                ed = Math.max(ed, p.y);  // 合并区间
            } else {
                st = p.x;
                ed = p.y;
            }
        }

        return st == 1 && ed == m;  // 检查是否覆盖了整个区间 [1, m]
    }

    // 二分查找满足条件的最小时间点
    static int binarySearch(Pair[] w, int m) {
        int l = 0, r = (int) 2e9;  // 初始化二分查找的左右边界
        while (l < r) {
            int mid = l + (r - l) / 2;  // 计算中间时间点
            if (check(w, m, mid)) {  // 如果当前时间点满足条件
                r = mid;  // 缩小右边界
            } else {
                l = mid + 1;  // 否则,增大左边界
            }
        }
        return r;  // 返回最小满足条件的时间点
    }
}

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值