N Queens
Last updated
Last updated
Then-queens puzzle is the problem of placingnqueens on ann×nchessboard such that no two queens attack each other.
Given an integern, return all distinct solutions to then-queens puzzle.
Each solution contains a distinct board configuration of then-queens' placement, where'Q'
and'.'
both indicate a queen and an empty space respectively.
For example, There exist two distinct solutions to the 4-queens puzzle:
[
[".Q..", // Solution 1
"...Q",
"Q...",
"..Q."],
["..Q.", // Solution 2
"Q...",
"...Q",
".Q.."]
]
Approach 1: Brute Force
using c_boards = vector<vector<vector<char>>>;
using c_board = vector<vector<char>>;
using c_row = vector<char>;
struct VectorHash {
size_t operator()(const c_board& board) const {
std::hash<char> hasher;
size_t seed = 0;
for (c_row row : board) {
for (char c : row) {
seed ^= hasher(c) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
}
return seed;
}
};
static class NQueens {
public:
c_boards solveNQueens(int num_q) {
unordered_set<c_board, VectorHash> unique_boards;
c_boards valid_boards;
unordered_map<int, pair<int, int>> chess_ids = get_chess_ids(num_q);
c_board blank_chess_board(num_q, vector<char>(num_q, '.'));
int ignore_next = num_q;
const int n = num_q * num_q;
const int k = num_q;
vector<int> current_queen_keys(n);
iota(current_queen_keys.begin(), current_queen_keys.end(), 1);
do
{
vector<pair<int, int>> q_locs = map_ids_to_queen_positions(chess_ids, current_queen_keys.begin(), current_queen_keys.begin() + k);
vector<c_row> next_board = map_queens(blank_chess_board, q_locs);
if (confirm_stalemate(next_board) && unique_boards.find(next_board) == unique_boards.end()) {
unique_boards.insert(next_board);
valid_boards.push_back(next_board);
print2d(next_board);
}
reverse(current_queen_keys.begin() + k, current_queen_keys.end());
} while (next_permutation(current_queen_keys.begin(), current_queen_keys.end()));
return valid_boards;
}
private:
unordered_map<int, pair<int, int>> get_chess_ids(int num_q) {
unordered_map<int, pair<int, int>> chess_location_ids;
const int num_elms = num_q * num_q;
chess_location_ids.reserve(num_elms);
int id = 1;
for (int i = 0; i < num_q; i++) {
for (int j = 0; j < num_q; j++) {
chess_location_ids[id++] = { i,j };
}
}
return chess_location_ids;
}
bool confirm_grounded_queen(const pair<int, int> q_pos, const c_board &board) {
int i, j;
for (i = q_pos.second + 1; i < board.size(); i++)
if (board[q_pos.first][i] == 'Q') return false; // right
for (i = q_pos.second - 1; i < board.size(); i--)
if (board[q_pos.first][i] == 'Q') return false; // left
for (i = q_pos.first + 1; i < board.size(); i++)
if (board[i][q_pos.second] == 'Q') return false; // down
for (i = q_pos.first - 1; i < board.size(); i--)
if (board[i][q_pos.second] == 'Q') return false; // up
for (i = q_pos.first - 1,
j = q_pos.second + 1;
i < board.size() && j < board.at(i).size();
i--, j++)
if (board[i][j] == 'Q') return false; // diagonal right up
for (i = q_pos.first + 1,
j = q_pos.second - 1;
i < board.size() && j < board.at(i).size();
i++, j--)
if (board[i][j] == 'Q') return false; // diagonal left down
for (i = q_pos.first + 1,
j = q_pos.second + 1;
i < board.size() && j < board.at(i).size();
i++, j++)
if (board[i][j] == 'Q') return false; // diagonal right down
for (i = q_pos.first - 1,
j = q_pos.second - 1;
i < board.size() && j < board.at(i).size();
i--, j--)
if (board[i][j] == 'Q') return false; // diagonal left up
}
bool confirm_stalemate(const c_board &board) {
for (int i = 0; i < board.size(); i++) {
for (int j = 0; j < board.size(); j++) {
if (board[i][j] == 'Q' && !confirm_grounded_queen({ i,j }, board))
return false;
}
}
return true;
}
template<typename iter>
vector<pair<int, int>> map_ids_to_queen_positions(const unordered_map<int, pair<int, int>> &key, iter id_start, iter id_end) {
vector<pair<int, int>> queen_locs; queen_locs.reserve(distance(id_start, id_end));
for (auto i = id_start; i != id_end; i++)
queen_locs.push_back(key.at(*i));
return queen_locs;
}
c_board map_queens(const c_board &blank_board, const vector<pair<int, int>> &q_pos) {
c_board board_cp = blank_board;
for (const pair<int, int> &p : q_pos)
board_cp[p.first][p.second] = 'Q';
return board_cp;
}
};
int main()
{
NQueens q;
for (int i : {1, 2, 3, 4, 5, 6, 7, 8, 9}) {
cout << i << " x " << i << " BOARD:" << endl;
q.solveNQueens(i);
}
}
Example Output
1 x 1 BOARD:
0: Q
2 x 2 BOARD:
3 x 3 BOARD:
4 x 4 BOARD:
0: . Q . .
1: . . . Q
2: Q . . .
3: . . Q .
0: . . Q .
1: Q . . .
2: . . . Q
3: . Q . .
5 x 5 BOARD:
0: Q . . . .
1: . . Q . .
2: . . . . Q
3: . Q . . .
4: . . . Q .
0: Q . . . .
1: . . . Q .
2: . Q . . .
3: . . . . Q
4: . . Q . .
0: . Q . . .
1: . . . Q .
2: Q . . . .
3: . . Q . .
4: . . . . Q
...
Check every permutable position on the chess board. This can be done by mapping each position on the board to a unique key that identifies it's i,j
position - and then checking all n(n)Pn arrangement's, where n is the number of queens.