Это не совсем симпатичное решение. На самом деле, это довольно сложно реализовать, но это, безусловно, дает полиномиальную сложность. Хотя сложность также велика (n ^ 5 * k - моя грубая оценка), кто-то может найти способ улучшить ее или найти здесь идею для лучшего решения. Или вам этого может быть достаточно: даже эта сложность намного лучше, чем грубая сила.
Примечание : оптимальное решение (установлено S
) с оболочкой H
включает все точки из оригинального набора внутри H
. В противном случае мы могли бы отбросить одну из граничных точек H
и включить эту пропущенную точку, уменьшив периметр.
( обновление точно так же, как 'оптимизация' mbeckish опубликовано)
Предположение : никакие две точки из множества не образуют вертикальную линию. Это может быть легко достигнуто вращением всего набора точек на некоторый иррациональный угол вокруг начала координат.
Из-за предположения, приведенного выше, любой сложный корпус имеет одну крайнюю левую и одну крайнюю правую точки. Кроме того, эти две точки делят корпус на top
и bottom
части.
Теперь давайте возьмем один сегмент из top
части этого корпуса и один из bottom
. Давайте назовем эти два сегмента middle segments
и периметр правой части этого корпуса - right
периметр.
Примечание : эти два сегмента - это все, что нам нужно знать о правой части нашего выпуклого корпуса, чтобы продолжить строить его слева. Но иметь только две точки вместо 4 недостаточно: мы не можем поддерживать условие «выпуклости» таким образом.
Это приводит к решению . Для каждого набора точек {p0, p1, p2, p3} и числа i
(i <= k) мы храним минимальный периметр <code>right, который может быть достигнут, если [p0, p1], [p2, p3] равны двум middle
сегментов и i
- количество точек в right
части этого решения (включая точки внутри него, а не только на границе).
Мы проходим все точки справа налево. Для каждой новой точки p
мы проверяем все комбинации точек {p0, p1, p2, p3} так, чтобы точка p могла продолжить эту оболочку влево (либо на top
, либо на bottom
части). Для каждого такого набора и размера i
мы уже храним оптимальный размер периметра (см. Параграф выше).
Примечание : если вы добавите точку p
к right-hull
, образованному точками {p0, p1, p2, p3}, вы увеличите размер набора i
хотя бы на 1. Но иногда это число будет> 1: вам нужно будет включить все точки в треугольник {p, p0, p2}. Они не на корпусе, а внутри него.
Алгоритм окончен :) Кроме того, несмотря на пугающую сложность, вы можете заметить, что не все сегменты [p0, p1], [p2, p3] могут быть middle
сегментами: это должно существенно сократить фактическое время вычислений.
update Это обеспечивает только оптимальный размер периметра, а не сам набор. Но найти набор просто: для каждого «состояния» выше вы сохраняете не только размер периметра, но и последнюю добавленную точку. Затем вы можете «проследить» ваше решение обратно. Это вполне стандартная уловка, я полагаю, это не проблема для вас, вы, кажется, хорошо разбираетесь в алгоритмах:)
update2 По сути, это DP (динамическое программирование), только немного раздутый