Применения выпуклых оболочек - Алгоритмика
Применения выпуклых оболочек

Применения выпуклых оболочек

авторы Константин Амеличев Сергей Слотин
обновлено авг. 27, 2021

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

В этой статье сильно недостает иллюстраций, но мы надеемся на ваше воображение.

#Локализация точки

Пусть у нас есть выпуклый многоугольник A0A1A2AnA_0 A_1 A_2 \ldots A_n и поступают запросы определить, находится ли точка PP внутри него.

Рассмотрим его триангуляцию на треугольники ΔA0A1A2,ΔA0A2A3,,ΔA0An1An\Delta A_0 A_1 A_2, \Delta A_0 A_2 A_3, \ldots, \Delta A_0 A_{n-1} A_n и решим чуть более сложную задачу: определить, к в каком их треугольников она лежит (если она вообще лежит внутри многоугольника).

Рассмотрим вектора A0A1,A0A2,,A0An\overrightarrow{A_0 A_1}, \overrightarrow{A_0 A_2}, \ldots, \overrightarrow{A_0 A_n} — диагонали, разбивающие многоугольник на треугольники. Заметим, что они отсортированы по углу — для определенности будем считать, что против часовой стрелки. Рассмотрим вектор A0P\overrightarrow{A_0 P} и найдем в этой последовательности из n1n-1 векторов первый, который находится «справа» от A0P\overrightarrow{A_0 P}. Это можно сделать бинарным поиском и проверкой через знак векторного умножения.

Пусть мы нашли такое 1i<n1 \leq i < n, что вектор A0P\overrightarrow{A_0 P} зажат между A0Ai\overrightarrow{A_0 A_i} и A0Ai+1\overrightarrow{A_0 A_{i+1}}. Тогда просто проверим, попала ли точка PP в треугольник ΔA0AiAi+1\Delta A_0 A_i A_{i+1} или нет. Если попала, то точка PP лежит внутри многоугольника и мы локализовали ее положение, иначе точка PP лежит снаружи многоугольника.

Каждый такой запрос мы обрабатываем за время O(logn)O(\log{n}).

#Поиск касательных, параллельных данной прямой

Пусть у нас есть выпуклый многоугольник и некоторая прямая. Мы хотим найти 22 прямые, которые касаются многоугольника и при этом параллельны данной прямой.

Найдем точки, в которых эти прямые будут касаться многоугольника. Рассмотрим вектор vv — направляющий вектор данной прямой. Рассмотрим вектора A0A1,A1A2,,AnA0\overrightarrow{A_0 A_1}, \overrightarrow{A_1 A_2}, \ldots, \overrightarrow{A_n A_0} — напомним, что вершины идут в порядке против часовой стрелки, а значит эти вектора будут отсортированы по полярному углу.

Тогда, если рассмотреть, между какими соседними позициями в этом порядке должны находиться вектора vv и v-v, то эти позиции будут соответствовать тем вершинам, в которых касательные касаются многоугольника. Для того, чтобы найти эти позиции, достаточно сделать lower_bound по последовательности векторов.

#Поиск касательных из точки

Пусть у нас опять есть выпуклый многоугольник A0A1AnA_0 A_1 \ldots A_n, но прямой нет, а есть некоторая точка PP. Мы хотим найти 22 точки AiA_i и AjA_j, такие что прямые PAiP A_i и PAjP A_j это касательные к многоугольнику — или сказать, что точка PP лежит внутри многоугольника.

Проводить проверку мы уже умеем за O(logn)O(\log n), так что предположим, что точка лежит снаружи.

Рассмотрим такую же последовательность ориентированных векторов-ребер и разделим её на две половины: будем называть ребро AiAi+1A_i A_{i+1} левым, если точка Ai+1A_{i+1} лежит слева от прямой PAiP A_i, иначе правым.

Дальше мы можем запустить от левой половины бинпоиск, который будет искать первую вершину AiA_i, для которой Ai+1A_{i+1} лежит справа от вектора PAiP A_i — ровно этой вершины и будет касаться касательная. Бинпоиск в правой половине делается аналогично.

#Пара наиболее отдаленных точек

Дан набор nn точек, и требуется найти среди них пару наиболее отдаленных.

Заметим, что оптимальная пара гарантированно лежит на выпуклой оболочке этого множества: если бы одна из точек лежала строго внутри выпуклой оболочки, то можно было бы вместо неё выбрать какую-то более отдаленную.

Построим выпуклую оболочку всех точек и пронумеруем её вершины в порядке обхода против часовой стрелки. Заметим, что если для ll-той точки самой отдаленной была rr-тая, то для (l+1)(l+1)-ой и всех дальнейших никакая точка раньше rr-той в обходе не может быть оптимальной.

Здесь возникает идея применить метод двух указателей: будем перебирать точку ll в оболочке и поддерживать оптимальную rr для неё, в цикле while проверяя, лучше ли получатся ответ для пары ll и (r+1)(r+1). Так как суммарно rr сделает не более O(n)O(n) увеличений, алгоритм будет работать за O(n)O(n) плюс время построения выпуклой оболочки.

#Треугольник максимальной площади

Дан набор nn точек, и требуется найти среди них треугольник с максимальной площадью.

Аналогично предыдущей задаче, все три точки гарантированно будут на выпуклой оболочке, так что сразу построим её.

Теперь заметим, что для фиксированного основания aa и bb, если мы увеличим его правую точку, то оптимальная третья точка cc будет иметь не меньший номер.

Значит, мы можем перебрать точку aa, а затем с помощью метода двух указателей перебрать bb, поддерживая оптимальный cc. Суммарно алгоритм будет работать за O(n2)O(n^2).