Back to Bit Manipulation
N-Queens (Bitmask)
N-Queens (Bitmask)
š§© Problem Statement
The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other.
Given an integer n, return the total number of distinct solutions to the n-queens puzzle.
This is an optimized version of the classic backtracking problem using Bitmasking.
š¹ Example 1:
Input:
n = 4
Output:
2
Explanation:
There are two distinct solutions:
[[0, 1, 0, 0], [0, 0, 0, 1], [1, 0, 0, 0], [0, 0, 1, 0]][[0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1], [0, 1, 0, 0]]
š¹ Example 2:
Input:
n = 1
Output:
1
š Approaches
1. Backtracking with Bitmasking
- Concept: Instead of using arrays or sets to track occupied columns and diagonals, we can use integers (bitmasks).
- State:
cols: Bitmask where the $i$-th bit is set if column $i$ is occupied.diag1: Bitmask for "major" diagonals (top-left to bottom-right). Computed asrow - col.diag2: Bitmask for "minor" diagonals (top-right to bottom-left). Computed asrow + col.- Optimization:
- When moving to the next row, the diagonal constraints shift.
diag1shifts left (<< 1) because a conflict at(r, c)moves to(r+1, c-1).diag2shifts right (>> 1) because a conflict at(r, c)moves to(r+1, c+1). (Or vice versa depending on implementation).- Algorithm:
availablePositions = ~(cols | diag1 | diag2) & ((1 << n) - 1)- Iterate through bits in
availablePositions. Beating the lowest set bit (p = P & -P) allows $O(1)$ extraction of the next position. - Recurse:
solve(row + 1, cols | p, (diag1 | p) << 1, (diag2 | p) >> 1)
ā³ Time & Space Complexity
- Time Complexity: $O(N!)$. The bitwise operations speed up the constant factor significantly compared to array-based tracking.
- Space Complexity: $O(N)$ for recursion stack.
š Code Implementations
C++
#include <iostream>
using namespace std;
class Solution {
public:
int totalNQueens(int n) {
return solve(0, 0, 0, 0, n);
}
private:
int solve(int row, int cols, int diag1, int diag2, int n) {
if (row == n) return 1;
int count = 0;
int availablePositions = ((1 << n) - 1) & ~(cols | diag1 | diag2);
while (availablePositions) {
int position = availablePositions & -availablePositions; // Get lowest set bit
availablePositions &= availablePositions - 1; // Remove lowest set bit
count += solve(row + 1, cols | position, (diag1 | position) << 1, (diag2 | position) >> 1, n);
}
return count;
}
};
Python
class Solution:
def totalNQueens(self, n: int) -> int:
self.count = 0
self.solve(0, 0, 0, 0, n)
return self.count
def solve(self, row, cols, diag1, diag2, n):
if row == n:
self.count += 1
return
available_positions = ((1 << n) - 1) & ~(cols | diag1 | diag2)
while available_positions:
position = available_positions & -available_positions
available_positions &= available_positions - 1
self.solve(row + 1, cols | position, (diag1 | position) << 1, (diag2 | position) >> 1, n)
Java
class Solution {
private int count = 0;
public int totalNQueens(int n) {
count = 0;
solve(0, 0, 0, 0, n);
return count;
}
private void solve(int row, int cols, int diag1, int diag2, int n) {
if (row == n) {
count++;
return;
}
int availablePositions = ((1 << n) - 1) & ~(cols | diag1 | diag2);
while (availablePositions != 0) {
int position = availablePositions & -availablePositions;
availablePositions &= availablePositions - 1;
solve(row + 1, cols | position, (diag1 | position) << 1, (diag2 | position) >> 1, n);
}
}
}
š Real-World Analogy
Laser Grid Security:
Placing sensors on a grid such that no two sensors are in the same row, column, or diagonal line of sight. Using bitmasks is like having a control panel where lit LEDs show blocked paths almost instantly.
šÆ Summary
ā
Bitmask State: Efficiently represent "occupied" columns and diagonals using single integers.
ā
Constraint Propagation: Shifting masks simulates row+1 movement perfectly.
Solution Code
O(n) TimeO(1) Space
#include <iostream>
using namespace std;
class Solution {
public:
int totalNQueens(int n) { return solve(0, 0, 0, 0, n); }
private:
int solve(int row, int cols, int diag1, int diag2, int n) {
if (row == n)
return 1;
int count = 0;
int availablePositions = ((1 << n) - 1) & ~(cols | diag1 | diag2);
while (availablePositions) {
int position = availablePositions & -availablePositions;
availablePositions &= availablePositions - 1;
count += solve(row + 1, cols | position, (diag1 | position) << 1,
(diag2 | position) >> 1, n);
}
return count;
}
};
int main() {
Solution sol;
cout << "Test Case 1 (n=4): " << sol.totalNQueens(4) << endl; // Expect 2
cout << "Test Case 2 (n=1): " << sol.totalNQueens(1) << endl; // Expect 1
cout << "Test Case 3 (n=8): " << sol.totalNQueens(8) << endl; // Expect 92
return 0;
}
SYSTEM STABLEUTF-8 ⢠STATIC_RENDER