Разбор задачи: N-Ферзей

Предложенный нами код:

 def solveNQueens(n):
        res = []
        def dfs(i, l, r, m, arr):
            if i == n:
                res.append(arr)
            else:
                l = l[1:] + [0]
                r = [0] + r[:-1]
                for j in range(n):
                    if m[j] == l[j] == r[j] == 0:
                        l[j] = r[j] = m[j] = 1 
                        dfs(i + 1, l, r, m, arr + [("." * j) + "Q" + ("." * (n - j - 1))])
                        l[j] = r[j] = m[j] = 0
        dfs(0, [0] * n, [0] * n, [0] * n, [])
        return res

1. Объявление функции

Функция solveNQueens принимает один аргумент n, который определяет размер шахматной доски (n x n). Возвращается список решений, где каждое решение — это массив строк, представляющих одну расстановку ферзей.

def solveNQueens(n):
    res = []

Здесь res — это список, в который будут добавлены все уникальные решения.


2. Вложенная функция dfs

Функция dfs выполняет поиск решений с помощью рекурсии.

def dfs(i, l, r, m, arr):
    if i == n:
        res.append(arr)

Аргументы функции:

  • i — текущая строка доски, на которой мы размещаем ферзя.
  • l — список, который отслеживает занятые позиции в "левых диагоналях" (главных диагоналях).
  • r — список для отслеживания занятых позиций в "правых диагоналях" (побочных диагоналях).
  • m — список, который отслеживает занятые столбцы.
  • arr — текущая сборка расстановки ферзей в виде массива строк.

Если i == n, значит, мы дошли до конца доски и нашли решение. Это решение добавляется в список res.


3. Обновление ограничений для диагоналей

При переходе к следующей строке обновляются списки l и r для отслеживания доступности диагоналей:

l = l[1:] + [0]
r = [0] + r[:-1]
  • l[1:] + [0] — смещает элементы на одну позицию вправо для отслеживания главной диагонали.
  • [0] + r[:-1] — смещает элементы на одну позицию влево для побочной диагонали.

4. Проверка доступности позиции

Для каждой позиции j на текущей строке проверяется, можно ли разместить ферзя:

for j in range(n):
    if m[j] == l[j] == r[j] == 0:

Позиция считается доступной, если:

  • m[j] == 0 — столбец свободен.
  • l[j] == 0 — главная диагональ свободна.
  • r[j] == 0 — побочная диагональ свободна.

5. Размещение ферзя

Если позиция доступна, ферзь временно размещается на этой клетке:

l[j] = r[j] = m[j] = 1
dfs(i + 1, l, r, m, arr + [("." * j) + "Q" + ("." * (n - j - 1))])
l[j] = r[j] = m[j] = 0
  • l[j] = r[j] = m[j] = 1 — помечаем, что столбец и диагонали заняты.
  • dfs(i + 1, ...) — переходим к следующей строке.
  • После возврата из рекурсии (backtracking) убираем отметки (l[j] = r[j] = m[j] = 0), чтобы попробовать другие варианты.

Строка с ферзем формируется так: ("." * j) + "Q" + ("." * (n - j - 1)). Например, если j = 2, для доски 4x4 получится строка "..Q.".


6. Инициализация рекурсии

Рекурсия запускается с начальным состоянием: пустые строки, свободные столбцы и диагонали:

dfs(0, [0] * n, [0] * n, [0] * n, [])

7. Возврат результата

После завершения всех рекурсивных вызовов возвращается список решений res:

return res

Пример работы

Ввод:

n = 4

Ход выполнения:

  1. На первой строке (i = 0) пробуем все 4 позиции.
  2. Если ферзь размещён успешно, переходим на следующую строку (i = 1).
  3. Повторяем процесс для каждой строки.
  4. Если найдено решение, добавляем его в res и откатываемся (backtracking) для поиска других решений.

Преимущества решения

  • Используется компактный и эффективный способ отслеживания занятых клеток.
  • Рекурсия с backtracking обеспечивает поиск всех возможных решений.
  • Решение легко адаптируется для других размеров доски.

 

 



Вы должны Войти или Зарегистрироваться чтобы оставлять комментарии