Count the ways to place queens that cannot attack

Backtracking Senior Python 3.13+ Reviewed By Best Latest Apps

Predict the output

What does this print?

Python 3.13
queens = [(0, 1), (1, 3), (2, 0), (3, 2)]
print(len({r - c for r, c in queens}), len({r + c for r, c in queens}))

Options

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A 4 4

Right: no two queens share either kind of diagonal, so this 4 × 4 board is a valid placement.

Two set sizes equal to n confirm a board in O(n), and the same keys let the search check each square in O(1).

Why not the others?
  • B 3 4. The four values of row - col are -1, -2, 2 and 1, all different.
  • C 4 3. The four values of row + col are 1, 4, 2 and 5, all different.

30-second answer

Place one queen per row. Keep sets of used columns and used diagonals (row - col and row + col identify them). In each row, try only free columns, recurse to the next row, then remove the queen. O(n · n!) is a loose worst-case bound, and the search is far below it in practice because the sets prune early. The sets and the recursion take O(n) space.

Practise saying it · 60 s. No recording, just a timer

Explain the queens search.

Say your answer out loud, then check which of these points you covered:

  • One queen per row
  • Sets for columns and diagonals
  • Undo when backtracking

Deep dive

One queen per row removes the row check entirely.

row - col is constant along one diagonal direction and row + col along the other.

Bitmasks instead of sets make this one of the fastest classic searches.

Python solution Ran on Python 3.13.5 · mypy --strict
Python 3.13
def count_boards(n: int) -> int:
    cols: set[int] = set()
    falling: set[int] = set()  # row - col
    rising: set[int] = set()  # row + col

    def place(row: int) -> int:
        if row == n:
            return 1
        total = 0
        for c in range(n):
            if c in cols or row - c in falling or row + c in rising:
                continue
            cols.add(c)
            falling.add(row - c)
            rising.add(row + c)
            total += place(row + 1)
            cols.remove(c)
            falling.remove(row - c)
            rising.remove(row + c)
        return total

    return place(0)

Common mistakes

  • Checking all earlier queens on every placement, which is O(n) per check instead of O(1).
  • Forgetting to remove the queen's column and diagonals when backtracking.

Senior take & follow-ups

Explain the diagonal keys; it is the trick that makes the checks O(1).

Follow-up questions

Think of your answer first, then open the hint.

  1. How many solutions for n = 8?

    Show hint

    92.

  2. How do you list the boards instead of counting?

    Show hint

    Keep the chosen column for each row and record a copy when the last row is filled.

What each operation costs: Depth First Search

From the author

DSA Master - Data & Algorithms

Free on iPhone and iPad: watch 50+ data structures and algorithms step through animations, run the code in the playground, and try the daily challenge.

Suggest a fix if something here is wrong or out of date.