来源:第十四届蓝桥杯省赛PythonB组\5407. 管道
题目描述:
题意理解:
每一段都有可开关的水阀和检测水流的传感器,但并不是每个水阀都会打开,只有输入的L(i)会在S(i)时刻打开,我们要求的就是水流满整个管道的最短时间。
思路:
假如在t时刻满足题目要求, 那么比t大的时刻也一定满足要求,具有单调性。我们就可以想到用二分来查找每个时刻,并将每个时刻每个水阀能检测的范围算出来。此时问题就变成了对于这些所检测出来的范围是否能覆盖整个管道,所以我们又想到将这些区间合并,看看该区间能否覆盖所有位置。
注意细节:
- 二分的数据范围:根据题目S(i) 所给的数据范围,左端点是1,右端点为2e9
- 此题的区间合并并不是两个区间有交集才能合并,例如[1, 2]和[3, 4]也可以进行合并成[1, 4]
- 注意数据范围,需要用long long
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
vector<vector<int>> w; //存Li水阀能在Si时刻打开
int n, m;
bool check(LL mid) {
vector<vector<int>> segs;
for (int i = 0; i < n ; i ++ ) {
int L = w[i][0], S = w[i][1];
if (S <= mid) { // 当前的时刻大于水闸打开的时刻,此时改水闸已经打开
// 当前水闸可以检测到的范围
int t = mid - S;
int l = max(1, L - t), r = min((LL)L + t, (LL)m); //这里也注意long long, int可能爆
segs.push_back({l, r});
}
}
sort(segs.begin(), segs.end()); //默认按左端点排序
int ed = -1, st = -1;
for(int i = 0; i < segs.size(); i ++) // 将所有水闸可以检测到的范围进行区间合并
{
if (segs[i][0] <= ed + 1) ed = max(ed, segs[i][1]);
else st = segs[i][0], ed = segs[i][1];
}
return ed == m && st == 1;
}
int main() {
cin >> n >> m; // 砸门数和管道数
for (int i = 0; i < n; i ++ ) {
int l, s;
cin >> l >> s;
w.push_back({l, s});
}
LL l = 0, r = 2e9;
while (l < r) { //二分查找最短时间
int mid = (LL) l + r >> 1; //用int可能会爆,记得使用long long
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << r ;
return 0;
}