Рубрики
Без рубрики

Как решить проблему ящериц ребенка – веселый поворот на проблеме N-Queens

Автор оригинала: FreeCodeCapm Team.

Сачин Малхотра

Это заявление о проблеме было назначением в составе моей курсовой работы для программы Masters в USC. У меня были множество веселых во время решения его, и я решил поделиться своими участиями с сообществом.

Начнем с оператора проблемы.

Проблема

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

Детская ящерица может выстрелить свой язык и съесть любую другую ящерицу для детей, прежде чем у вас будет время, чтобы спасти его. Таким образом, вы хотите убедиться, что ни одна ящерица не может съесть другую ящерицу ребенка в питомнике (BURP).

Для каждой ящерицы ребенка вы можете разместить их в одном месте на сетке. Оттуда они могут выстрелить их язык вверх, вниз, слева, справа и по диагонали. Их языки очень долго и могут добраться до края питомника из любого места.

На рисунке 1 показано, в каких способах есть ящерица для ребенка.

Помимо детских ящериц, ваш питомник, возможно, посадил в нем некоторые деревья. Ваши ящерицы не могут снимать свои языки сквозь деревья, и вы не можете переместить ящерицу в одно и то же место, что и дерево.

Таким образом, дерево заблокирует любую ящерицу от еды другой ящерицы, если она находится на пути. Кроме того, дерево заблокирует вас от перемещения ящерицы в это место.

На рисунке 2 показаны различные допустимые договоренности ящериц:

Учитывая договоренность о деревьях, нам нужно выводить новое расположение ящериц, так что ни одна ящерица не может есть еще одну. Вы не можете двигать ни одно из деревьев.

Вы можете найти весь код для этого здесь Отказ

Сходство с N-Queens

Эта проблема очень похожа на классическую N-Queens Проблема Отказ Давайте переведем некоторые ограничения в проблеме N-Queens.

  • Там может быть только одна королева на ряд и колонну.
  • Может быть только одна королева на по диагонали и против диагонали.
  • Учитывая приведенные выше 2 ограничения, мы не можем разместить больше королев, чем количество строк или количество столбцов.

Теперь мы добавляем в небольшой твист, который говорит, что у нас есть деревья в определенном месте в питомнике (чтение шахматной доски), а королевы (ящерицы) по обе стороны от дерева не могут атаковать друг друга. Это меняет вещи большим временем.

  • Теперь мы можем иметь несколько ящериц на строку, на столбцу.
  • Точно так же мы можем иметь несколько ящериц в одном диагонали или против диагонали.
  • Мы можем разместить больше количества ящериц, чем количество рядов или столбцов.

Хотя проблема выглядит очень похоже на стандартную головоломку размещения n Queens на плате N * N, решение и сложность оказывается совсем разным в целом.

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

Итак, здесь, в этом посте, мы обсудим высоко оптимизированное обратное решение.

Backtracking C ++

Решение Backtracking для этой проблемы работает аналогично обратному раствору для стандартных проблем N-Queens.

Решение для этой проблемы основано на следующей идее.

Учитывая клетку [я, j] Мы можем либо поместить ящеру, либо не разместить ящерицу. Любой из наших вариантов может привести к решению. Итак, мы попробуем оба.

Самый большой инвариант в этом алгоритме в том, что мы всегда двигаемся слева направо через доску.

Предположим, есть дерево в месте [3,4]. Его эффект маскировки (если таковая имеется) будет видимым только после того, как мы перейдем к ячейке [3,4] в нашей рекурсии и движусь вперед. Не до этого.

Прежде чем мы добраться до фактического псевдокода для проблемы, есть некоторые другие компоненты алгоритма, которые я хотел бы объяснить. Это сделало бы понимание псевдокода намного проще.

Проверка безопасности и O (1) Conundrum

Если вы посмотрели на Моя предыдущая статья Это обсуждает разные алгоритмические решения для головоломки N-Queens, вы можете понять, какова проблема действительно.

Мы получаем почти 5-кратное улучшение скорости на 12 * 14 шахматной доске, где мы должны разместить 14 королев, после преобразования функции проверки безопасности на O (1) от O (n). Поэтому стоит провести время, чтобы выяснить алгоритм, который бы сказал нам в постоянном времени, если безопасно разместить королеву на данную ячейку [I, J].

Для справки давайте посмотрим на то, как мы сделали это в обычные N-Queens.

Мы использовали некоторые дополнительные структуры данных, чтобы сказать нам, если королева была помещена в определенную диагональную, антидиагональную, строку или столбец в O (1) время и использование их, мы могли бы сказать, было ли безопасно разместить королеву на Данная ячейка [I, J].

Однако, если вы прочитали постановление о проблеме. Из двух диагоналей), то на самом деле это безопасно разместить ящерицу на текущей ячейке. Это связано с тем, что дерево маскирует атаку, заставляя камеру безопасным для новой ящерицы.

Это меняет вещи, много?.

Начнем с каких структур данных, которые нам нужны для реализации.

Используемые структуры данных

Давайте перейдем на них один за другим.

  • Tree_locations – Это просто словарь, который сообщает нам, если данная ячейка [I, J] содержит дерево. Это населено вправо в начале нашего решателя.
  • Четыре структуры данных строки, столбцы, диагонали и анти-диагонали используются для простого сообщите нам, если есть ящерица в соответствующей R, C, R - C, R + C соответственно. Однако для этой проблемы они представляют целочисленные ценности, а не логические. Эти четыре структуры данных хранят либо 1 или -1 в зависимости от того, если мы помещаем ящерицу в текущей ячейке [I, j], или мы столкнулись с деревом на данной ячейке [I, j]. Таким образом, рекурсия продолжается от одной ячейки к другой и может либо столкнуться с деревом на данной ячейке [I, J], либо может столкнуться с пустой ячейкой, в этом случае мы должны позвонить в IS_CELL_SAFE Функция для проверки, если мы можем разместить ящерицу. Я приду, как ценности обновляются в этих четырех структурах данных, а именно Ряды , Колонны , Диагонали и анти-диагонали позже.
  • IS_THERE_QUEEN_IN_THIS_COLUMN Это словарь, который просто хранит количество ящериц, которые мы поместили в данный столбец. Это используется как часть обрезка эвристического, используемого для уменьшения размера пространства поиска.
  • next_position_same_column – Это говорит нам о каждом [i, j], что такое следующее место в том же колонке, где мы могли бы попробовать новую ящерию. В нормальных проблемах N-Queens мы можем разместить одну королеву только в колонне, но в этом случае мы можем иметь несколько королев (ящериц). Итак, после размещения ящерицы на ячейке [I, J], нам нужно место первого дерева в том же колонке и сказать, что это [k, j]. Следующее доступное место для размещения ящерицы в этой колонке будет затем [K + 1, J]. Этот массив используется как часть этой оптимизации.
  • Наконец, IS_THERE_A_TREE_AHEAD Это словарь, который говорит нам, если есть дерево где-то в праве после этого столбца (включая этот столбец). Это также населена однажды как часть начального предварительной обработки. Это также используется как часть обрезки эвристики, упомянутой выше, описывающей IS_THERE_QUEEN_IN_THIS_COLUMN Отказ

Функция предварительной обработки

Функция предварительной обработки изначально называется до того, как наш алгоритм запускает выполнение, и все это наполняет, наполняет некоторые из структур данных, обсуждаемых выше.

  • tree_populator Функция довольно простая. Заполняет словари Tree_locations и IS_THERE_A_TREE_AHEAD Отказ
  • Функция find_next_largesgegs Учитывает каждый столбец, состоящий из 0S и 2, где A 0 представляет собой пустую ячейку, а A 2 представляет собой дерево. Для каждой клетки он выясняет следующий крупнейший элемент или другие слова, ближайшее дерево в это место в этой колонке. Мы называем find_next_largesgegs Функция Для каждой колонны на доске.

Для лучшего понимания этого алгоритма обратитесь к Этот обзор Отказ

IS_CELL_SAFE Функция

Положительное значение в любом из словарей ряд , Колонка , Диагональ , антиагональю Значит, есть ящерица, которая потенциально будет атаковать еще одну ящерицу, которую мы пытаемся разместить на [ROW, CONOL].

Эта функция выглядит очень похожей на тот, который мы использовали для нормальных N-Queens. Важная часть заключается в том, как мы обновляем значения в этих структурах данных.

Марк посетил, Уничик посетил и хешютинг

Функция Hash_util Это общая функция, используемая для обновления значений для всех четырех структур данных (а именно Rows , Колонны , Диагинали и анти-диагонали ).

Эта функция называется оба, когда мы отмечаем ящерицу или дерево, или, когда мы безыскиваемся любой из них. Маркировка и безмятежность просто обрабатываются до рекурсивного вызова и отслеживания того, что мы обработали, после окончания рекурсивного вызова.

Помните, что инвариант, обсуждаемый в этой проблеме: мы переходим слева направо через доску. Как только мы столкнулись с деревом в определенном месте (i, j) во время рекурсии, оно будет защищать ящерицы друг от друга для всех клеток [I + 1, J] и всех столбцов k> j.

Результат Переменная очень важна здесь. Например, мы столкнулись с деревом, скажем [3,0], и в [1,0] была ящерица. Теперь движусь вперед, это дерево маскирует эффект ящерицы в [1,0] – по крайней мере для этого столбца – и нам нужно где-то привлечь этот эффект.

Итак, в этом случае:

  • IS_Marking = Правда ,
  • Is_tree = Правда ,
  • Словарь = Колонка ,
  • Словарь [Col] > 0 (мы храним 1 всякий раз, когда мы помещаем ящерицу в этой колонне). Это потому, что мы уже поместили ящерицу в колонне 0 (при 1,0), и раньше не было обнаружено дерево, что бы скрыть эффект ящерицы для ячейки [3,0] в нашем примере.
  • value_to_add . = -1 (для дерева –1, для ящерицы это 1)

Так что сейчас, Словарь [Col] = -1, и мы возвращаем 1 в качестве результата означает, что возникает дерево в данном (строке, столбце), на самом деле имеет эффект маскировки. Нам нужно записать этот эффект маскирования, потому что это будет использоваться во время отмены после рекурсии.

Теперь рассмотрим два других функция, которые образуют основной компонент алгоритма.

Марк посетил

Мы называем эту функцию в двух случаях. Один, когда мы столкнулись с деревом, и, как мы хотим разместить ящерицу. Таким образом, соответственно мы использовали булевую переменную, чтобы сказать нам, почему эта функция была вызвана.

В случае дерева мы устанавливаем значение -1, в противном случае это +1. Затем мы обновляем четыре структуры данных. Логика одинакова для всех четырех из них. Это просто ключ, который меняется для каждого.

Помните, Row - Col используется для уникального идентификации диагонали и Row + Col используется для уникальной идентификации против диагонали.

Также обратите внимание, что мы храним четырехместные значения возврата для четырех структур данных в Did_tree_affect Словарь. Это давайте узнаем, если встретите дерево в расположении (ROW, COL), имел любое влияние вообще то есть маскировка. Эти данные используются во время операции отмены.

Посещение без приглашений

Мы знаем, что положительная ценность в любом из четырех словарей означает, что данная ячейка не безопасна для размещения ящерицы.

Операция отмены довольно проста для ящерица Отказ Если мы звоним Unmark_visized Функция для ящерицы, это означает, что клетка была достаточно безопасной, прежде чем мы разместили там ящерицу, поэтому мы просто поставим значение -1 во всех четырех словарях. (Помните, положительное значение в любом из строк, столбцы, диагонали или антидидиагоналы сломают функцию IS_CELL_SAFE Функция для этой ячейки)

В случае функции Unmark_visized был призван для дерева, мы извлекаем значения из Did_tree_affect Для данного [ROW, COL] и используйте эти значения, чтобы вернуть словари. Смысл в этом является то, что предположим, что мы столкнулись с деревом при заданном [ряд, col] и оно замаскировало эффект ящерицы для диагонали и колонны, движущейся вперед. Смотрите следующий рисунок:

Когда мы должны вернуть видимость дерева в рекурсии, мы в основном должны вернуть его к маскирующему эффекту. Это то, что Did_tree_affect Словарь используется для.

Теперь, когда у нас есть все наши словари, мы можем, наконец, посмотреть на фактическую функцию DFS, которая делает всю нашу большую подъем для поиска решения.

Отступление решателя

Кодекс, по-видимому, сложно, и пост станет чрезвычайно долго, если бы я начал объяснять его подробно. Я мог бы уточнить сомнения в разделе комментариев. На данный момент я напишу подробную версию псевдокода для завершения.

1. Start at the cell (0, 0)
2. For a given cell (i, j)     a. If all the lizards have been placed, print the solution and return True.
b. Check if the current cell has a tree.          b1. Call mark_visited function to update the 4 dictionaries with possible masking effects due to this tree.
c. If the current cell isn't a tree and a lizard can be placed         c1. Call mark visited for [i, j] as a lizard.         c2. Add [i, j] to the solution set.          c3. Increment column j as containing one more lizard.         c4. Find the next row number to recurse on in the column j. If there is such a row number say r, then recurse on [r, j]. Else recurse on [0, j+1]         c5. Unmark the current cell. Call function unmark_visited for [i, j]         c6. Decrement column j as it contains one less lizard now.
d. We may want to have a branch in our recursive solution where we did not place a lizard at [i, j] and simply moved forward. OR, we couldn't place a lizard at [i, j] and we now have to move forward.          d1. if [i + 1] < n, recurse on [i+1, j]         d2. else [PRUNING HEURISTIC]              d2.1 check if                    * we did not place any lizard in the current col.                   * there is no tree in the current col and ahead.                    * number of lizards left to be placed are more than the number of columns left.                    * If yes to all 3, then BACKTRACK.              d2.2 Else, recurse on [0, j+1]
e. If the current cell was in-fact a tree, then call unmark_visited to undo its effects.

Это самый APT псевдокод, который я мог бы придумать для решателя на основе DFS. Это именно то, как функция DFS структурирован.

С этой логикой крупнейший тестовый случай, который я смог решить, заключается в размещении 97 000 ящериц на доске 1000 * 1000. Было около 2 секунд, чтобы бежать.

Теперь я говорю вам, что это может звучать как огромный подвиг, но на самом деле это не. Это было довольно легко для алгоритма. Вопрос для вас, ребята, чтобы выяснить почему за этим? Дайте мне знать в разделе комментариев!

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

Надеюсь, вам понравилась статья и наслаждалась так же, как я сделал, решил эту проблему. Если вам понравилось этот пост, распространите любовь (❤) как можно больше. Ваше здоровье!