Программа курса:
Разбор задачи: 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 res1. Объявление функции
Функция 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
Ход выполнения:
- На первой строке (i = 0) пробуем все 4 позиции.
- Если ферзь размещён успешно, переходим на следующую строку (i = 1).
- Повторяем процесс для каждой строки.
- Если найдено решение, добавляем его в
resи откатываемся (backtracking) для поиска других решений.
Преимущества решения
- Используется компактный и эффективный способ отслеживания занятых клеток.
- Рекурсия с backtracking обеспечивает поиск всех возможных решений.
- Решение легко адаптируется для других размеров доски.