acwing5407管道:对题解的理解

#如何看待IBM中国研发部裁员?#

1.5407. 管道 - AcWing题库

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;    
}

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值