Skip to the content.

Сканирующая прямая - алгоритм, часто применяемый, когда в задаче фигурируют отрезки на прямой или события во времени.

Наибольшее покрытие

Дано $n$ отрезков на прямой, и необходимо найти точку, покрытую наибольшим числом отрезков.

Нетрудно заметить, что среди подходящих точек обязательно будут края отрезков, поэтому рассматривать в качестве ответа можно только их.

Решение в тупую с асимптотикой $O(n^2)$ - перебираем все концы отрезков и для каждого за $O(n)$ считаем количество отрезков, покрывающих данную точку.

Улучшим решение. Можно перебирать края отрезков в порядке увеличения их координаты, поддерживая, сколько отрезков в данный момент “открыто”. Делать это можно с помощью счетчика: если встречаем начало отрезка — $+1$, иначе — $-1$.

Тогда ответом будет та точка, для которой счётчик был наибольшим.

Таким образом мы получим асимптотику $O(n log n)$ из-за сортировки.

Приведём пример кода:

events = []
for a, b in segments:
    events.append((a, -1))
    events.append((b, 1))

events.sort()

cnt = 0
best = 0
ans = -1
for event in events:
    cnt -= event[1]
    if cnt > best:
        best = cnt
        ans = event[0]

print(ans)

Как сортировать события

Стоит сказать пару слов о том, в каком порядке рассматривать события.

Если в одной точке происходит несколько событий разных типов (в нашем случае один отрезок закрылся, а другой открылся), то иногда надо будет сначала открыть новый отрезок, а потом закрыть старый (в нашем случае мы так и делаем, потому что такая точка принадлежит и закрывшемуся отрезку, и открывшемуся). На это влияет порядок сортировки по значению типа события. В данном примере $-1$ — (отрезок открывается), $1$ — (отрезок закрывается).