题目
有一根长度为 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
代码与思路
java
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; // 返回最小满足条件的时间点
}
}