Я готовился к предстоящему соревнованию по программированию и наткнулся на вопрос, который меня совершенно сбил с толку. Тем не менее, я чувствую, что эту концепцию я должен изучить сейчас, а не скрещивать пальцы, что она никогда не придет в голову.
В основном это фигура коня на шахматной доске. У вас есть два входа: начальное и конечное местоположение. Затем цель состоит в том, чтобы вычислить и распечатать кратчайший путь, по которому рыцарь может добраться до целевого места.
Я никогда не занимался вопросами кратчайшего пути и даже не знаю, с чего начать. Какую логику я использую для решения этой проблемы?
PS Если это имеет какое-то отношение, они хотят, чтобы вы дополняли обычные ходы коня, позволяя ему перемещаться к четырем углам квадрата, образованного (потенциально) восемью ходами, которые может сделать конь, учитывая, что центр поля находится местонахождение рыцаря.
источник
Ответы:
Здесь у вас есть график, где все доступные ходы связаны (значение = 1), а недоступные ходы отключены (значение = 0), разреженная матрица будет иметь вид:
Кратчайший путь из двух точек на графике можно найти с помощью http://en.wikipedia.org/wiki/Dijkstra's_algorithm.
Псевдокод со страницы википедии:
РЕДАКТИРОВАТЬ:
Introduction to Algorithms
ISBN 0-262-03384-4. Или вы можете попробовать википедию, http://en.wikipedia.org/wiki/List_of_algorithmsисточник
РЕДАКТИРОВАТЬ: см. Ответ Саймона , где он исправил формулу, представленную здесь.
На самом деле существует формула O (1)
Это изображение, которое я сделал, чтобы его визуализировать (квадраты, до которых может дотянуться конь на N- м ходу, окрашены в один цвет).
Вы можете заметить здесь закономерность?
Хотя мы можем видеть закономерность, действительно сложно найти функцию,
f( x , y )
которая возвращает количество ходов, необходимых для перехода от квадрата( 0 , 0 )
к квадрату.( x , y )
Но вот формула, которая работает, когда
0 <= y <= x
Примечание: этот вопрос был задан на SACO 2007, день 1,
и решения здесь.
источник
2 * floor((y - delta) / 3) + delta
иdelta - 2 * floor((delta - y) / 4)
. Это официальное решение со страницы конкурса, но оно неверное. Это первое уравнение (изif
) возвращает неправильные ответы. На шахматной доске [-1000..1000] x [-1000..1000], которая имеет размер 2001x2001 (но логически неограничен), данный ответ насчитывает 2,669,329 из 4,004,001 правильных полей (66,66%). Кто-нибудь знает рабочее решение без каких-либо петель?Вот правильное решение O (1), но для случая, когда конь движется только как шахматный конь, и на бесконечной шахматной доске:
https://jsfiddle.net/graemian/5qgvr1ba/11/
Ключ к обнаружению этого - заметить закономерности, которые появляются, когда вы рисуете доску. На диаграмме ниже число в квадрате - это минимальное количество ходов, необходимых для достижения этого квадрата (вы можете использовать поиск в ширину, чтобы найти это):
Поскольку решение симметрично по осям и диагоналям, я нарисовал только случай x> = 0 и y> = x.
Нижний левый блок - это начальная позиция, а числа в блоках представляют минимальное количество ходов для достижения этих блоков.
Обратите внимание на 3 закономерности:
(Убедитесь, что вы видите оба набора диагоналей как верхний левый и нижний правый. У них постоянное количество ходов. Диагонали нижнего левого верхнего правого угла намного сложнее.)
Вы можете вывести формулы для каждого. Желтые блоки - особые случаи. Итак, решение становится:
с самыми сложными вертикальными группами:
См. Скрипку для других случаев.
Может быть, я пропустил более простые или элегантные узоры? Если так, я бы с удовольствием их увидел. В частности, я заметил некоторые диагональные узоры в синих вертикальных корпусах, но я не исследовал их. Тем не менее, это решение по-прежнему удовлетворяет ограничению O (1).
источник
Очень интересная проблема, с которой я столкнулся недавно. После поиска некоторых решений я попытался восстановить аналитическую формулу (
O(1) time and space complexity
), приведенную в решениях SACO 2007 Day 1 .Прежде всего, я хочу поблагодарить Грэма Пайла за очень красивую визуализацию, которая помогла мне исправить формулу.
По какой-то причине (может быть, для упрощения или красоты или просто по ошибке) они перенесли
minus
знак вfloor
оператор, в результате получилась неправильная формулаfloor(-a) != -floor(a) for any a
.Вот правильная аналитическая формула:
Формула работает для всех пар (x, y) (после применения осей и диагональной симметрии) за исключением угловых (1,0) и (2,2), которые не удовлетворяют шаблону и жестко запрограммированы в следующем фрагменте:
function distance(x,y){ // axes symmetry x = Math.abs(x); y = Math.abs(y); // diagonal symmetry if (x < y) { t = x;x = y; y = t; } // 2 corner cases if(x==1 && y == 0){ return 3; } if(x==2 && y == 2){ return 4; } // main formula var delta = x-y; if(y>delta){ return delta - 2*Math.floor((delta-y)/3); } else{ return delta - 2*Math.floor((delta-y)/4); } } $body = $("body"); var html = ""; for (var y = 20; y >= 0; y--){ html += '<tr>'; for (var x = 0; x <= 20; x++){ html += '<td style="width:20px; border: 1px solid #cecece" id="'+x+'_'+y+'">'+distance(x,y)+'</td>'; } html += '</tr>'; } html = '<table>'+html+'</table>'; $body.append(html);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Примечание: jQuery используется только для иллюстрации, код см. В
distance
функции.источник
Да, Дейкстра и BFS дадут вам ответ, но я думаю, что шахматный контекст этой проблемы дает знания, которые могут дать решение, которое намного быстрее, чем общий алгоритм кратчайшего пути, особенно на бесконечной шахматной доске.
Для простоты опишем шахматную доску как плоскость (x, y). Цель состоит в том, чтобы найти кратчайший путь от (x0, y0) до (x1, y1), используя только возможные шаги (+ -1, + -2), (+ -2, + -1) и (+ -2 , + -2), как описано в PS вопроса
Вот новое наблюдение: нарисуйте квадрат с углами (x-4, y-4), (x-4, y + 4), (x + 4, y-4), (x + 4, y + 4) . Этот набор (назовем его S4) содержит 32 точки. Кратчайший путь от любой из этих 32 точек до (x, y) требует ровно двух ходов .
Кратчайший путь от любой из 24 точек множества S3 (определяется аналогично) до (x, y) требует как минимум двух ходов .
Следовательно, если | x1-x0 |> 4 или | y1-y0 |> 4, кратчайший путь от (x0, y0) до (x1, y1) ровно на два хода больше, чем кратчайший путь от (x0, y0) до S4. И последнюю проблему можно быстро решить с помощью простой итерации.
Пусть N = max (| x1-x0 |, | y1-y0 |). Если N> = 4, то кратчайший путь от (x0, y0) до (x1, y1) имеет ceil (N / 2) шагов.
источник
Ответ O (1) выше [ https://stackoverflow.com/a/8778592/4288232 Мустафа Сердар Шанлы] на самом деле не работает. (Отметьте (1,1) или (3,2) или (4,4), кроме очевидных крайних случаев (1,0) или (2,2)).
Ниже представлено гораздо более уродливое решение (python), которое действительно работает (с добавленными «тестами»):
источник
above
или наbelow
самом деле не работает на SO.Что вам нужно сделать, так это представить возможные ходы коня в виде графика, где каждая позиция на доске является узлом, а возможные ходы в другую позицию - ребром. Алгоритм Дейкстры не нужен, потому что каждое ребро имеет одинаковый вес или расстояние (все они так же просты или коротки). Вы можете просто выполнить поиск BFS от начальной точки до конечной позиции.
источник
Решение из первых принципов на Python
Впервые я столкнулся с этой проблемой в тесте Codility. Мне дали 30 минут на ее решение - мне потребовалось значительно больше времени, чтобы получить такой результат! Проблема заключалась в следующем: сколько ходов нужно коню, чтобы перейти от 0,0 к x, y, используя только допустимые ходы коня. x и y были более или менее неограниченными (поэтому мы не говорим здесь о простой шахматной доске 8x8).
Они хотели решение O (1). Мне нужно было решение, в котором программа явно решала проблему (т. Е. Я хотел чего-то более очевидного, чем шаблон Грэма - шаблоны имеют привычку ломаться там, где вы не смотрите), и я действительно хотел не полагаться на неоспоримая формула, как в решении Мустафы
Итак, вот мое решение, чего оно стоит. Начнем, как и другие, с того, что отметим, что решение симметрично относительно осей и диагоналей, поэтому нам нужно решать только для 0> = y> = x. Для простоты объяснения (и кода) я собираюсь обратить проблему: конь начинает с x, y и стремится к 0,0.
Предположим, мы сократили проблему до окрестности источника. Мы узнаем, что на самом деле означает «близость», со временем, а пока давайте просто запишем некоторые решения в шпаргалке (происхождение внизу слева):
Итак, учитывая x, y в сетке, мы можем просто считать количество ходов в начало координат.
Если мы начали за пределами сетки, мы должны вернуться к ней. Мы вводим «среднюю линию», которая представляет собой линию, представленную y = x / 2. Любой конь в позиции x, y на этой линии может вернуться к шпаргалке, используя серию ходов на 8 часов (то есть: (-2, -1) ходов). Если x, y лежит выше средней линии, тогда нам понадобится последовательность движений на 8 часов и 7 часов, а если она лежит ниже средней линии, нам понадобится последовательность из 8 часов и 10 часов. Часы идут. Здесь следует отметить две вещи:
Итак, давайте посмотрим на движения выше средней линии. Мы утверждаем, что:
(dx; dy) = (2,1; 1,2) (n8; n7) (матричная запись, без математического набора - вектор-столбец (dx; dy) равен квадратной матрице, умноженной на вектор-столбец (n8; n7) - количество ходов на 8 часов и количество ходов на 7 часов), и аналогично;
(dx; dy) = (2,2; 1, -1) (n8; n10)
Я утверждаю, что dx, dy будут примерно равны (x, y), поэтому (x-dx, y-dy) будет в непосредственной близости от начала координат (независимо от того, какой окажется «близость»).
Две строки в коде, которые вычисляют эти термины, являются решением этих проблем, но они выбраны так, чтобы иметь некоторые полезные свойства:
(Хотите доказательства этого?) Итак, расстояние до Рыцаря будет суммой n7, n8, n10 и шпаргалки [x-dx, y-dy], и наша шпаргалка сводится к следующему:
Но это еще не конец истории. Посмотрите на 3 в нижнем ряду. Единственные способы достичь этого:
Аналогичную оптимизацию можно провести с цифрой 4 вверху справа. Помимо начала, единственный способ добраться до него - это переместиться на 8 часов от точки (4,3). Этого нет в шпаргалке, но если бы он был там, его расстояние было бы 3, потому что вместо этого мы могли бы привязать 7 часов к (3,1), что имеет расстояние только 2. Итак, мы должны отследить один Переместитесь на 8 часов, а затем перейдите на 7 часов вперед.
Итак, нам нужно добавить в шпаргалку еще одно число:
(Примечание: существует множество оптимизаций обратного отслеживания из (0,1) и (0,2), но поскольку решатель никогда не приведет нас туда, нам не нужно о них беспокоиться.)
Итак, вот некоторый код Python для оценки этого:
Между прочим, если вы хотите узнать реальный маршрут, этот алгоритм также обеспечивает это: это просто последовательность из n7 7-часовых ходов, за которыми следуют (или перемежаются) n8 8-часовых ходов, n10 10- o'clock движется, и любой танец продиктован шпаргалкой (которая сама по себе может быть в шпаргалке).
Теперь: как доказать, что это правда. Недостаточно просто сравнить эти результаты с таблицей правильных ответов, потому что сама задача неограниченна. Но мы можем сказать, что если расстояние коня до квадрата s равно d, то если {m} - это набор разрешенных ходов от s, расстояние коня (s + m) должно быть либо d-1, либо d + 1. для всех м. (Вам нужно доказательство этого?) Кроме того, должен быть хотя бы один такой квадрат с расстоянием d-1, если только s не является началом координат. Итак, мы можем доказать правильность, показав, что это свойство выполняется для каждого квадрата. Таким образом:
В качестве альтернативы, мы можем доказать правильность любого квадрата s, пройдя путь от спуска до начала координат. Сначала проверьте s на разумность, как указано выше, затем выберите любое s + m такое, что distance (s + m) == d-1. Повторяйте, пока не дойдете до начала координат.
Howzat?
источник
Я еще не изучал графики ... что касается проблемы реализации этого с помощью простых массивов, я не мог найти никакого другого решения, кроме этого. Я рассматривал позиции не как ряды и файлы (обычная шахматная запись), а как индексы массива. К вашему сведению, это только для шахматной доски 8 * 8. Всегда приветствуются любые советы по улучшению.
* Комментарии должны быть достаточными для вашего понимания логики. Однако вы всегда можете спросить.
* Проверено на компиляторе DEV-C ++ 4.9.9.2 (Bloodshed Software).
источник
Думаю, это тоже может вам помочь ..
и используя динамическое программирование, чтобы получить решение.
PS: Он вроде как использует BFS без необходимости объявлять узлы и ребра графа.
источник
Вот решение этой конкретной проблемы, реализованное на Perl. Он покажет один из кратчайших путей - в некоторых случаях их может быть несколько.
Я не использовал ни один из описанных выше алгоритмов - но было бы неплохо сравнить его с другими решениями.
источник
источник
Просто рубиновый код из jsfiddle ответа Грэма Пайла выше , выделил весь дополнительный код и преобразовал оставшийся в рубин, чтобы получить решение по его алгоритму, похоже, работает. Тем не менее, все еще тестирую:
Единственное намерение - сэкономить время на преобразовании кода, если кому-то нужен полный код.
источник
вот версия PHP функции Жюля Мэя
источник
Вот моя программа. Это не идеальное решение. В функцию рекурсии нужно внести множество изменений. Но конечный результат идеален. Пытался немного оптимизировать.
источник
Вот версия C, основанная на коде Мустафы Сердара Шанлы, которая работает для конечной доски:
Проверьте это здесь с доказательством против рекурсивного решения
источник