ā N-Queens Problem
š“ AdvancedBacktracking
š Problem Statement
Place N queens on an NĆN chessboard such that no two queens attack each other. Queens can attack horizontally, vertically, and diagonally. Return all distinct solutions.
š“ Approach: Backtracking with Pruning
<?php
function solveNQueens(int $n): array {
$result = [];
$board = array_fill(0, $n, array_fill(0, $n, '.'));
// Optimization: track occupied columns & diagonals
$cols = array_fill(0, $n, false);
$diag1 = array_fill(0, 2*$n, false); // row+col
$diag2 = array_fill(0, 2*$n, false); // row-col+n
placeQueens($board, 0, $n, $cols, $diag1, $diag2, $result);
return $result;
}
function placeQueens(array &$board, int $row, int $n,
array &$cols, array &$diag1, array &$diag2, array &$result): void {
if ($row === $n) {
$result[] = array_map(fn($r)=>implode('',$r), $board);
return;
}
for ($col = 0; $col < $n; $col++) {
if ($cols[$col] || $diag1[$row+$col] || $diag2[$row-$col+$n]) {
continue; // Prune! This column/diagonal is attacked
}
// Place queen
$board[$row][$col] = 'Q';
$cols[$col] = true;
$diag1[$row+$col] = true;
$diag2[$row-$col+$n] = true;
placeQueens($board, $row+1, $n, $cols, $diag1, $diag2, $result);
// Backtrack
$board[$row][$col] = '.';
$cols[$col] = false;
$diag1[$row+$col] = false;
$diag2[$row-$col+$n] = false;
}
}
// Print solutions for N=4
$solutions = solveNQueens(4);
foreach ($solutions as $i => $sol) {
echo "Solution " . ($i+1) . ":\n";
foreach ($sol as $row) echo " $row\n";
}
// Solution 1: Solution 2:
// .Q.. ..Q.
// ...Q Q...
// Q... ...Q
// ..Q. .Q..
echo "Total solutions for n=4: " . count($solutions); // 2
echo "For n=8: " . count(solveNQueens(8)); // 92
š Complexity
| Worst-case (no pruning) | O(N!) |
| With pruning | Much less - ~92 for N=8, 724 for N=10 |
| Space | O(N²) for board + O(N) for tracking arrays |
šÆ Key Insight: Diagonal Tracking
Instead of checking entire diagonals (O(N) per check), use arrays:
- Main diagonal (/): All cells on same diagonal have same (row + col) value
- Anti-diagonal (\): All cells on same diagonal have same (row - col) value
This reduces the safety check from O(N) to O(1)!
š¼ Interview Discussion
- Why backtracking? We need all solutions, and greedy/DP don't apply. Backtracking systematically explores the state space.
- Why prune? Without pruning, we'd try N^N placements. Pruning reduces it to N! worst case (placing one queen per row).
- Can we do better? For counting solutions only (not printing), there are mathematical formulas. But for generating all solutions, backtracking is optimal.