算法 - 区间合并


题目: ACwing 1343. 挤牛奶

题目来源: https://www.acwing.com/problem/content/1345/

描述:

每天早上 5 点,三名农夫去牛场给奶牛们挤奶。

现在从 5 点开始按秒计时,第一名农夫在第 300 秒开始给牛挤奶,并在第 1000 秒停止挤奶。

第二名农夫在第 700 秒开始给牛挤奶,并在第 1200 秒停止挤奶。

第三名农夫在第 1500 秒开始给牛挤奶,并在第 2100 秒停止挤奶。

从开始挤奶到挤奶完全结束,这一期间,至少存在一名农夫正在挤奶的连续时间段的长度最长为 900 秒(第 300 秒至第 1200 秒),完全没有任何农夫在挤奶的连续时间段的长度最长为 300 秒(第 1200 秒至第 1500 秒)。

现在给你 N 名农夫挤 N 头奶牛的工作时间表,请你求出:

至少存在一名农夫正在挤奶的连续时间段的最长长度。
没有任何农夫在挤奶的连续时间段的最长长度。
注意:本题中给出的所有时间均为时刻(时间点),因此在本题中挤奶区间 [100,200] 和 [201,300] 中间会有长度为 1 秒的间歇时间。

输入格式

第一行包含整数 N,表示农夫数量。

接下来 N 行,每行包含两个非负整数 l,r,表示农夫挤奶的开始时刻和结束时刻。

输出格式

共一行,包含两个整数,分别表示最长连续挤奶时间以及最长连续无人挤奶时间。

数据范围

$1≤N≤5000,$
$1≤l≤r≤10^6$

输入样例:

1
2
3
4
3
300 1000
700 1200
1500 2100

输出样例:

1
900 300

算法1:$O(nlog n)$

区间合并问题

首先按照开始时间时间点进行排序。

然后遍历一遍:

如果当前区间的左端点大于上一个区间的右端点: 更新空闲区间的最大值,并更新区间为当前区间
否则更新区间的最长长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <algorithm>

#define l first
#define r second

using namespace std;
const int N = 5010;
typedef pair<int, int> PII;
PII a[N];
int n;
int free_time, long_time;


int main()
{
cin >> n;
for(int i = 0; i < n; i++) cin >> a[i].l >> a[i].r;

sort(a, a + n);

int l = a[0].l, r = a[0].r;
for(auto c : a)
{
if (c.l > r)
{
free_time = max(free_time, c.l - r);
l = c.l, r = c.r;
}
else r = max(r, c.r);
long_time = max(long_time, r - l);
}

cout << long_time << ' ' << free_time << endl;
return 0;
}

由于排序为$O(nlog n)$, 所以总的时间复杂度为$O(nlog n)$。