Читать «Bash IT Happens Истории ## 5401 – 5500» онлайн - страница 34

Bash.org.ru IT

#5484: О барометре в свободном падении

19:45 17.02.2011, IT happens

Говорите, программа рассчитывает принадлежность клетки к одной или другой группе по ? Ха! Нас этим не удивить.

Начало девяностых, олимпиада по информатике для школьников в далёкой провинции России. Одно из заданий: построить и вывести на экран решение задачи о выпуклой оболочке для произвольного массива из не менее чем трёх точек в двухмерном пространстве, никакие три из которых не лежат на одной прямой. Решением должен быть список точек, составляющих эту выпуклую оболочку, в произвольном порядке. В качестве бонуса можно было продемонстрировать всю эту чушь в графическом виде.

Для справки: задача важная, нужная и популярная, и решений у неё есть много. Одно из решений приведено в книге «Алгоритмы: построение и анализ» Т. Кормена, являющейся университетским учебником для большинства вузов, начиная с MIT. Но в девяностых на окраине России про Кормена ещё не знали, и пришлось выкручиваться своими силами.

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

Итак, для получения бонусных очков надо показать всё это графически. Отлично: выводим на экран все введённые точки, между всеми ними рисуем линии. Тогда линии, составляющие выпуклую оболочку, тоже будут нарисованы. Теперь берём какую-нибудь точку, расположенную вне этой оболочки ([639, 479] кажется подходящим кандидатом) и выполняем заливку FloodFill кавайно-малиновым цветом. Заливка упрётся в линии выпуклой оболочки. Теперь ещё раз пройдёмся по всем возможным линиям, отрисовывая их уже чёрным цветом — с точки зрения пользователя линии сотрутся. На экране останется малиновый фон с чёрной кляксой посередине, а граница между ними как раз и будет внешней оболочкой. Бонусное задание выполнено.

Теперь для каждой точки найдём цвета восьми окружающих её пикселей. Если среди них окажутся малиновые, вызовем сложный комплекс проверок, призванный ответить на вопрос, является ли точка частью внешней оболочки или же просто лежит рядом с границей. Я не помню, как я обрабатывал граничные случаи, — кажется, сдвигал точку на пиксель в сторону чёрного цвета, перерисовывал и смотрел на разницу, — но в конечном итоге я находил все точки выпуклой оболочки (плюс, возможно, несколько лишних, лежащих совсем рядом с границей и не отсеянных дополнительными проверками). В конце концов я очищал экран и рисовал выпуклую оболочку набело, одной линией: мол, смотрите, завидуйте. Весь процесс занимал несколько минут.