@makaleks

Как определить ближайших соседей прямоугольника с заданной стороны на поле с пропусками, без полного перебора?

Пусть задан одномерный массив прямоугольников, они могут касаться друг-друга, могут нет. Пересечений нет. Порядок прямоугольников следует интуитивному алгоритму: по возрастанию с приоритетом оси Y больше X, начало координат сверху слева, область видимости ограничена сверху и снизу - то есть прямоугольники лежат как-бы построчно. 'Сетки' нет, координаты вещественные. Поиск остановить, как только найдены все соседи, перекрывающие целевую сторону.

Сабж в теме. Дополняю формулировку примером с иллюстрации:

63f0b5b796d1a801587668.png

На рисунке разыскиваются соседи восьмого прямоугольника по левой стороне. Это должны быть прямоугольники 5,7,9,12,13. Прямоугольники дальше/раньше даже не рассматривать, что визуально кажется очевидным.

Какие мысли:
1. надо рассматривать каждую отдельную сторону, и делать от целевого прямоугольника некоторое количество шагов по массиву вперёд и/или назад - алгоритм кажется плюс-минус одинаков
2. вроде как нужен счётчик по стороне в виде вектора
[{rect_idx, {side_segment_used_p1, side_segment_used_p2}}]
, так можно будет остановиться по достижении 'конца' и не учитывать прямоугольники, которые не деляют вклада в этот счётчик (в 'красные линии' на иллюстрации)
3. сортировку прямоугольников (построчное размещение) я уже ввёл, без неё совсем мрак

Где-то тут и застрял. Кажется многовато выходит сущностей. А ведь нужен ещё какой-то учёт ситуации, когда 'вклад в сторону' делает не сосед-прямоугольник, а ось координат (стенка). Строгая формулировка критерия остановки. Может ещё что-то. Идеально, если это решённая задача, которую я просто не осилил нагуглить.

Спасибо!
  • Вопрос задан
  • 192 просмотра
Решения вопроса 1
wataru
@wataru Куратор тега Алгоритмы
Разработчик на С++, экс-олимпиадник.
Эта задача решается алгоритмом "сканирующая прямая".

Представьте что у есть вертикальная линия, которая двигатеся слева-направо непрерывно. В каждый момент времени на прямой могут появиться какие-то прямоугольники, могут закончиться какие-то прямоугольники или ничего не поменяется - в этот момент прямая какие-то прямоугольники пересекает.

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

Тут подойдет что-нибудь вроде std::set в C++ - структура хранящая упорядоченное множество объектов, с доступом и изменением за логарифм, умеющая искать ближайший к заданному значению слева и справа (lower_bound, upper_bound). Хранить в ней мы будем вертикальные отрезки, помеченные номерами их прямоугольникв. Не знаю ее аналоги в других языках - нужно что-то реализованное или на binary search tree, или на skip list.

Когда прямоугольник открывается надо посмотреть, с какими отрезками в set данный отрезок пересекается. Все эти отрезки надо добавить в ответ, удалить полностью покрываемые новым отрезком, а торчащие из него укоротить. Таким образом мы будем поддерживать множество "видимых" отрезков с прямой - ближайших к ней слева.

В этой задаче можно забить на правые границы прямоугольников и на их ширину (раз нет пересечений). Соседями каждого отрезка - левого края будут ближайшие к нему левые края каких-то прямоугольников.
Поэтому вам надо получить все отрезки заданные в виде {x, y1, y2, id} и отсортировать их (сначала по x, потом по y). Потом в этом порядке их обходите и применяйте новый отрезок к структуре данных. Все удаляемые отрезки + 2 пересекающихся сверху и снизу пойдут в список соседних для нового отрезка.

Этот алгоритм за O(n log n) получит всех соседей для всех прямоугольников.
Это что-то похожее вот на эту задачку с leetcode
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы