Перед тем, как научиться строить выпуклые оболочки, разберем несколько задач, которые можно решать с их помощью.
В этой статье сильно недостает иллюстраций, но мы надеемся на ваше воображение.
#Локализация точки
Пусть у нас есть выпуклый многоугольник и поступают запросы определить, находится ли точка внутри него.
Рассмотрим его триангуляцию на треугольники и решим чуть более сложную задачу: определить, к в каком их треугольников она лежит (если она вообще лежит внутри многоугольника).
Рассмотрим вектора — диагонали, разбивающие многоугольник на треугольники. Заметим, что они отсортированы по углу — для определенности будем считать, что против часовой стрелки. Рассмотрим вектор и найдем в этой последовательности из векторов первый, который находится «справа» от . Это можно сделать бинарным поиском и проверкой через знак векторного умножения.
Пусть мы нашли такое , что вектор зажат между и . Тогда просто проверим, попала ли точка в треугольник или нет. Если попала, то точка лежит внутри многоугольника и мы локализовали ее положение, иначе точка лежит снаружи многоугольника.
Каждый такой запрос мы обрабатываем за время .
#Поиск касательных, параллельных данной прямой
Пусть у нас есть выпуклый многоугольник и некоторая прямая. Мы хотим найти прямые, которые касаются многоугольника и при этом параллельны данной прямой.
Найдем точки, в которых эти прямые будут касаться многоугольника. Рассмотрим вектор — направляющий вектор данной прямой. Рассмотрим вектора — напомним, что вершины идут в порядке против часовой стрелки, а значит эти вектора будут отсортированы по полярному углу.
Тогда, если рассмотреть, между какими соседними позициями в этом порядке должны находиться вектора и , то эти позиции будут соответствовать тем вершинам, в которых касательные касаются многоугольника. Для того, чтобы найти эти позиции, достаточно сделать lower_bound
по последовательности векторов.
#Поиск касательных из точки
Пусть у нас опять есть выпуклый многоугольник , но прямой нет, а есть некоторая точка . Мы хотим найти точки и , такие что прямые и это касательные к многоугольнику — или сказать, что точка лежит внутри многоугольника.
Проводить проверку мы уже умеем за , так что предположим, что точка лежит снаружи.
Рассмотрим такую же последовательность ориентированных векторов-ребер и разделим её на две половины: будем называть ребро левым, если точка лежит слева от прямой , иначе правым.
Дальше мы можем запустить от левой половины бинпоиск, который будет искать первую вершину , для которой лежит справа от вектора — ровно этой вершины и будет касаться касательная. Бинпоиск в правой половине делается аналогично.
#Пара наиболее отдаленных точек
Дан набор точек, и требуется найти среди них пару наиболее отдаленных.
Заметим, что оптимальная пара гарантированно лежит на выпуклой оболочке этого множества: если бы одна из точек лежала строго внутри выпуклой оболочки, то можно было бы вместо неё выбрать какую-то более отдаленную.
Построим выпуклую оболочку всех точек и пронумеруем её вершины в порядке обхода против часовой стрелки. Заметим, что если для -той точки самой отдаленной была -тая, то для -ой и всех дальнейших никакая точка раньше -той в обходе не может быть оптимальной.
Здесь возникает идея применить метод двух указателей: будем перебирать точку в оболочке и поддерживать оптимальную для неё, в цикле while
проверяя, лучше ли получатся ответ для пары и . Так как суммарно сделает не более увеличений, алгоритм будет работать за плюс время построения выпуклой оболочки.
#Треугольник максимальной площади
Дан набор точек, и требуется найти среди них треугольник с максимальной площадью.
Аналогично предыдущей задаче, все три точки гарантированно будут на выпуклой оболочке, так что сразу построим её.
Теперь заметим, что для фиксированного основания и , если мы увеличим его правую точку, то оптимальная третья точка будет иметь не меньший номер.
Значит, мы можем перебрать точку , а затем с помощью метода двух указателей перебрать , поддерживая оптимальный . Суммарно алгоритм будет работать за .