ā™› 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.