2.读题,这道题要算时间,暴力的写法就是从时间1开始每次看管道是否全部通水,由题意,要让所有的传感器报警,是不是有最左边的半段和最右边的半段不用算呢?事实上这个想法是错误的,因为水每秒钟走一段,而最后返回的秒数不存在二分之一,所以直接向上取整即可
3.主函数暴力:从0到2*len(我试了m不成功)枚举每一个时间,直到水管灌满,但是由于数据太多超时了,而想要优化枚举,只有滑动窗口,二分,双指针三条路,很明显只剩下二分,所以这道题要用二分优化(很多人看到这道题用二分会很突兀,确实这道题乍一看跟二分一点关系没有,只不过由于数据太大越界了才不得已选择了二分)
4.二分思路:先来看看单调性,随着时间从1到2*len,管道逐渐被灌满,且灌满后时间依次增加,符合单调性,以首次被灌满的点为中间点,左侧没灌满,右侧灌满了,符合二段性,
5.二分模板选择:找左,找右,找中,由于找第一次灌满的时间在右侧区间最左侧,且中间点,左侧,右侧不具有三段性,所以不找中,不找右,找左
6.judge函数:思路很简单,在规定时间内找到开闸时间并计算水的范围,并且防止越界,以下为演示:
小盆菇为水管,阳光盆为水流,时间为两秒,两闸门位于2位置和8位置,在0秒开闸
初始:
0秒
一秒
两秒
记录的区间分别是[1,4],[6,9]
接下来就是题解的难点,我看了很久才看懂:合并区间逻辑:
合并区间很简单,左侧位于管子左边,右侧位于管子右边即可,在对范围进行排序后(先比first再比second符合默认pair的比较规则,不需要另写函数)
以下是两种特殊情况:
情况1:重叠
记录范围很简单,但是会遇到这种重叠的情况,这种情况就说明,更新要用到前一个范围的头指针与后一个范围的尾指针来更新范围
但是又有一种新的情况
这就对范围更新提出了条件(排序后出现这种情况一定不行),必须第二个范围的first小于等于第一个范围的second
完事,来看代码(复现题解思路成功了)
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n, len;
vector<pair<long long, long long>> sign;
bool judge(long long time) {
vector<pair<long long, long long>> s;
for (auto valve : sign) {
long long position = valve.first;
long long openTime = valve.second;
if (openTime <= time) {
long long reach = time - openTime;
long long leftEnd = max(1LL, position - reach);
long long rightEnd = min(static_cast<long long>(len), position + reach);
s.push_back({leftEnd, rightEnd});
}
}
if (s.empty()) return false;
sort(s.begin(), s.end());
long long st = s[0].first, ed = s[0].second;
for (size_t i = 1; i < s.size(); ++i) {
if (s[i].first <= ed + 1) {
ed = max(ed, s[i].second);
} else {
if (st == 1 && ed == len) return true;
st = s[i].first;
ed = s[i].second;
}
}
return st == 1 && ed == len;
}
int main() {
cin >> n >> len;
for (int i = 0; i < n; i++) {
long long a, b;
cin >> a >> b;
sign.push_back({a, b});
}
long long left = 1;
long long right = 2 * len;
while (left < right) {
long long mid = left + (right - left) / 2;
if (judge(mid)) {
right = mid;
} else {
left = mid + 1;
}
}
cout << left << endl;
return 0;
}