N-Queens: place one row at a time
Picture
After placing a queen in row 0, later queens cannot share its column or either diagonal.
Predict
Why is row absent from the attacked sets?
Walk through the small example
We place exactly one queen per row. Choosing exactly one queen per row leaves columns and two diagonal identities as the only conflicts.
Mental model and complete Python solution
The row is the recursion depth. A candidate needs three tickets—column, down diagonal, up diagonal. Take them together, explore, then return all three.
What the function promises: Return the number of solutions for n >= 0. The empty board has one empty arrangement.
def count_queens(n):
if n < 0: raise ValueError("Negative n")
columns, down, up = set(), set(), set()
def place(row):
if row == n: return 1
total = 0
for column in range(n):
marks = column, row-column, row+column
if marks[0] in columns or marks[1] in down or marks[2] in up: continue
columns.add(marks[0]); down.add(marks[1]); up.add(marks[2])
total += place(row+1)
columns.remove(marks[0]); down.remove(marks[1]); up.remove(marks[2])
return total
return place(0)
assert count_queens(0) == 1
assert count_queens(4) == 2
assert count_queens(5) == 10
Work and storage: Worst-case exponential search; sets and recursion use O(n) active space.
When to recognize it: Choosing exactly one queen per row leaves columns and two diagonal identities as the only conflicts.
Check before applying it: Forgetting one undo corrupts sibling branches; symmetry optimizations require careful counting.
Change one rule
Return each board, not only the count.
See what changes
Store the chosen column per row and render it only at complete placements.
Connect
backtracking: Choose one legal option, recurse, then undo.
sets: Constant-time membership checks summarize used attack lines.