Files
hello-algo/ru/docs/chapter_backtracking/n_queens_problem.md
Yudong Jin 22b3b568ef fix Ru translation (#1894)
* docs(ru): replace prose quotes with guillemets

* docs(ru): replace prose semicolons with periods

* docs(ru): align animation title forms

* docs(ru): align figure and table references
2026-04-14 18:10:12 +08:00

7.6 KiB
Raw Blame History

Задача о n ферзях

!!! question

Согласно правилам шахмат ферзь может атаковать фигуры, находящиеся с ним на одной строке, в одном столбце или на одной диагонали. Даны $n$ ферзей и шахматная доска размера $n \times n$. Требуется найти такие расстановки, при которых ни одна пара ферзей не может атаковать друг друга.

Как показано на рисунке ниже, при n = 4 существует два решения. С точки зрения поиска с возвратом доска размера n \times n содержит n^2 клеток, которые образуют все возможные выборы choices . По мере поочередного размещения ферзей состояние доски непрерывно меняется, и текущее содержимое доски образует состояние state .

Решения задачи о 4 ферзях

На рисунке ниже показаны три ограничения этой задачи: несколько ферзей не могут находиться на одной строке, в одном столбце или на одной диагонали. При этом нужно помнить, что диагонали бывают двух типов: главная \ и побочная / .

Ограничения задачи о n ферзях

Построчная стратегия размещения

Число ферзей и число строк доски одинаково и равно n , поэтому легко получить следующий вывод: в каждой строке доски разрешено и нужно разместить ровно одного ферзя.

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

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

Построчная стратегия размещения

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

Обрезка по столбцам и диагоналям

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

!!! tip

Обратите внимание: начало координат матрицы находится в левом верхнем углу, при этом индексы строк растут сверху вниз, а индексы столбцов - слева направо.

Как теперь обработать ограничения по диагоналям? Пусть клетка на доске имеет координаты (row, col) . Выбрав некоторую главную диагональ в матрице, можно заметить, что разность индексов строки и столбца одинакова для всех клеток этой диагонали, то есть для всех клеток главной диагонали значение row - col постоянно.

Это означает, что если для двух клеток выполняется равенство row_1 - col_1 = row_2 - col_2 , то они обязательно лежат на одной и той же главной диагонали. Используя это правило, можно с помощью массива diags1 , показанного на рисунке ниже, отмечать наличие ферзя на каждой главной диагонали.

Аналогично для всех клеток побочной диагонали значение row + col является постоянным. Поэтому для обработки ограничений по побочным диагоналям можно использовать еще один массив diags2 .

Обработка ограничений по столбцам и диагоналям

Реализация кода

Заметим, что в квадратной матрице размера n диапазон значений row - col равен [-n + 1, n - 1] , а диапазон значений row + col равен [0, 2n - 2] . Следовательно, число главных и побочных диагоналей равно 2n - 1 , а значит, длины массивов diags1 и diags2 тоже равны 2n - 1 .

[file]{n_queens}-[class]{}-[func]{n_queens}

Если размещать ферзей построчно n раз, учитывая ограничение по столбцам, то начиная с первой строки и заканчивая последней мы получаем соответственно n, n-1, \dots, 2, 1 вариантов выбора, что дает O(n!) времени. При записи решения нужно скопировать матрицу state и добавить ее в res , а копирование требует O(n^2) времени. Следовательно, общая временная сложность равна $O(n! \cdot n^2)$ . На практике обрезка по диагональным ограничениям дополнительно сильно уменьшает пространство поиска, поэтому фактическая эффективность часто лучше этой оценки.

Массив state использует O(n^2) пространства, а массивы cols , diags1 и diags2 используют по O(n) пространства. Максимальная глубина рекурсии равна n , что требует O(n) памяти стека. Следовательно, пространственная сложность равна $O(n^2)$ .