这道题用的就是二分+区间合并的思想。
首先就是我自己做的思路,但是这个思路只能过70%,如果数据点大了还是会TLE,我没有办法优化......作者这里就是运用区间合并的方法进行做的。
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<algorithm>
#include<stack>
#include<queue>
#include<sstream>
#include<map>
#include<limits.h>
#include<set>
#define MAX 100
#define _for(i,a,b) for(int i=a;i<(b);i++)
#define ALL(x) x.begin(),x.end()
using namespace std;
typedef long long LL;
typedef pair<LL, LL> PII;
LL n, m, counts, num;
vector<PII>guanzi;
void merge(vector<PII>&haha) {
vector<PII>res;
sort(haha.begin(), haha.end());
LL st = INT_MIN;
LL ed = INT_MIN;
for (auto seg : haha) {
if (ed < seg.first-1) {//[1,2],[3,4]可以视为连通
if (st != INT_MIN)
res.push_back({ st,ed });
st = seg.first;
ed = seg.second;
}
else
ed = max(ed, seg.second);
}
if (st != INT_MIN)
res.push_back({ st,ed });
haha = res;
}//区间合并
bool check(LL k) {
vector<PII>res;
for (auto seg : guanzi) {
res.push_back({ seg.first - (k - seg.second),seg.first + (k - seg.second)});
}
merge(res);
return res.size()&&res[0].first <= 0 && res[0].second >= m;
}//对于管道是否全部疏通的判断
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
cin >> n >> m;
for (LL i = 0; i < n; i++)
{
LL a, b;
cin >> a >> b;
guanzi.push_back({ a,b });
}
LL left = 0;
LL right = 2e9;
while (left < right) {
LL mid=left+right>>1;
if (check(mid)) right = mid;
else
left = mid + 1;
}
cout << right;
return 0;
}
说一下这个程序的思路:首先就是我们知道,这个管道分为L段,我们需要让全部管道都通水。
但是这里就只给了管道的几个段,然后让这些段在一定范围内根据时间的限制进行通水。大家又开始疑惑了,我怎么知道是哪个时刻让水管都能通水呢?巧了,我也不知道,那么怎么办呢?
那我们就直接一个一个去试,不行就换,所以,我们会想到二分的做法,也就是快速遍历所给的数据范围里有没有能够满足这个条件的最早时刻。好了,解决完时刻的问题,我们需要解决水管的通水问题了。每段水管在某一时刻都有通水的范围,也就是一段区间,那么这些区间合并起来能够通完整个水管,这个时刻不就是答案了么?是的,就是这个思路,所以才会进行计算每段管道的通水范围,同时合并所给出的管道的范围。判断是否能够覆盖整个水管就行了。
作者这里思路是对的,但是不会优化,就很麻烦,所以看了一下y总的视频,发现思路是一样的,但是我编程的能力还没到那个水平上,现在来讲讲y总相对于我的代码优化了哪些地方:
1.在数据上,作者并没有管什么负数或者大于m的数,直接统统合并上,这就导致了数据范围过大,就算用LL也是数据范围挺大。优化后的代码是对于区间两端的数进行了判断,这样数据处理起来就没那么棘手了。
2.可能是刚学区间合并的问题,现在并不能灵活的使用区间合并模板,现在还只是模仿阶段。其实并不用传送数据至另一个空间里,直接就区间的两端进行判断就行了,两端能够是[1,m]就够了,其他的操作显然是画蛇添足了。
3.数据的范围判断上,在二分的时候,我们需要注意又有可能有10e9的结果,所以right需要开到2e9,这样才能保证不会爆出int范围,其他的数据更新也是如此,需要有LL支持。
上优化后的代码:
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<algorithm>
#include<stack>
#include<queue>
#include<sstream>
#include<map>
#include<limits.h>
#include<set>
#define MAX 100010
#define _for(i,a,b) for(int i=a;i<(b);i++)
#define ALL(x) x.begin(),x.end()
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
LL n, m, counts, num;
PII w[MAX], q[MAX];
bool check(int mid) {
int cnt = 0;
for (int i = 0; i < n; i++) {
int L = w[i].first;//管道的段
int S = w[i].second;//开始时刻
if (S <= mid) {
int t = mid - S;//相差的差值
int l = max(1, L - t);//对于<0的部分不会要
int r = min((LL)m, (LL)L + t);//对于大于m的部分也不要
q[cnt++] = { l,r };
}
}
sort(q, q + cnt);
int st = -1;
int ed = -1;
for (int i = 0; i < cnt; i++) {
if (q[i].first <= ed + 1)ed = max(ed, q[i].second);
else st = q[i].first, ed = q[i].second;
}
return st == 1 && ed == m;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
cin >> n >> m;
for (int i = 0; i < n; i++)
cin >> w[i].first >> w[i].second;
int l = 0;
int r = 2e9;
while (l < r) {
int mid = (LL)l + r >> 1;
if (check(mid))r = mid;
else
l = mid + 1;
}
cout << r << endl;
return 0;
}