mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-16 00:46:06 +00:00
build
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -9,14 +9,14 @@ icon: material/map-marker-path
|
||||
|
||||
!!! abstract
|
||||
|
||||
Like explorers in a maze, we may encounter obstacles on our path forward.
|
||||
We are like explorers in a maze, and may encounter difficulties on the path forward.
|
||||
|
||||
The power of backtracking lets us begin anew, keep trying, and eventually find the exit leading to the light.
|
||||
The power of backtracking allows us to start over, keep trying, and eventually find the exit leading to light.
|
||||
|
||||
## Chapter contents
|
||||
|
||||
- [13.1 Backtracking algorithms](backtracking_algorithm.md)
|
||||
- [13.2 Permutation problem](permutations_problem.md)
|
||||
- [13.3 Subset sum problem](subset_sum_problem.md)
|
||||
- [13.4 n queens problem](n_queens_problem.md)
|
||||
- [13.1 Backtracking Algorithm](backtracking_algorithm.md)
|
||||
- [13.2 Permutations Problem](permutations_problem.md)
|
||||
- [13.3 Subset-Sum Problem](subset_sum_problem.md)
|
||||
- [13.4 N-Queens Problem](n_queens_problem.md)
|
||||
- [13.5 Summary](summary.md)
|
||||
|
||||
@@ -2,59 +2,59 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 13.4 n queens problem
|
||||
# 13.4 N-Queens Problem
|
||||
|
||||
!!! question
|
||||
|
||||
According to the rules of chess, a queen can attack pieces in the same row, column, or diagonal line. Given $n$ queens and an $n \times n$ chessboard, find arrangements where no two queens can attack each other.
|
||||
According to the rules of chess, a queen can attack pieces that share the same row, column, or diagonal line. Given $n$ queens and an $n \times n$ chessboard, find a placement scheme such that no two queens can attack each other.
|
||||
|
||||
As shown in Figure 13-15, there are two solutions when $n = 4$. From the perspective of the backtracking algorithm, an $n \times n$ chessboard has $n^2$ squares, presenting all possible choices `choices`. The state of the chessboard `state` changes continuously as each queen is placed.
|
||||
As shown in Figure 13-15, when $n = 4$, there are two solutions that can be found. From the perspective of the backtracking algorithm, an $n \times n$ chessboard has $n^2$ squares, which provide all the choices `choices`. During the process of placing queens one by one, the chessboard state changes continuously, and the chessboard at each moment represents the state `state`.
|
||||
|
||||
{ class="animation-figure" }
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Figure 13-15 Solution to the 4 queens problem </p>
|
||||
<p align="center"> Figure 13-15 Solution to the 4-queens problem </p>
|
||||
|
||||
Figure 13-16 shows the three constraints of this problem: **multiple queens cannot occupy the same row, column, or diagonal**. It is important to note that diagonals are divided into the main diagonal `\` and the secondary diagonal `/`.
|
||||
Figure 13-16 illustrates the three constraints of this problem: **multiple queens cannot be in the same row, the same column, or on the same diagonal**. It is worth noting that diagonals are divided into two types: the main diagonal `\` and the anti-diagonal `/`.
|
||||
|
||||
{ class="animation-figure" }
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Figure 13-16 Constraints of the n queens problem </p>
|
||||
<p align="center"> Figure 13-16 Constraints of the n-queens problem </p>
|
||||
|
||||
### 1. Row-by-row placing strategy
|
||||
### 1. Row-By-Row Placement Strategy
|
||||
|
||||
As the number of queens equals the number of rows on the chessboard, both being $n$, it is easy to conclude that **each row on the chessboard allows and only allows one queen to be placed**.
|
||||
Since both the number of queens and the number of rows on the chessboard are $n$, we can easily derive a conclusion: **each row of the chessboard allows and only allows exactly one queen to be placed**.
|
||||
|
||||
This means that we can adopt a row-by-row placing strategy: starting from the first row, place one queen per row until the last row is reached.
|
||||
This means we can adopt a row-by-row placement strategy: starting from the first row, place one queen in each row until the last row is completed.
|
||||
|
||||
Figure 13-17 shows the row-by-row placing process for the 4 queens problem. Due to space limitations, the figure only expands one search branch of the first row, and prunes any placements that do not meet the column and diagonal constraints.
|
||||
Figure 13-17 shows the row-by-row placement process for the 4-queens problem. Due to space limitations, the figure only expands one search branch of the first row, and all schemes that do not satisfy the column constraint and diagonal constraints are pruned.
|
||||
|
||||
{ class="animation-figure" }
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Figure 13-17 Row-by-row placing strategy </p>
|
||||
<p align="center"> Figure 13-17 Row-by-row placement strategy </p>
|
||||
|
||||
Essentially, **the row-by-row placing strategy serves as a pruning function**, eliminating all search branches that would place multiple queens in the same row.
|
||||
Essentially, **the row-by-row placement strategy serves a pruning function**, as it avoids all search branches where multiple queens appear in the same row.
|
||||
|
||||
### 2. Column and diagonal pruning
|
||||
### 2. Column and Diagonal Pruning
|
||||
|
||||
To satisfy column constraints, we can use a boolean array `cols` of length $n$ to track whether a queen occupies each column. Before each placement decision, `cols` is used to prune the columns that already have queens, and it is dynamically updated during backtracking.
|
||||
To satisfy the column constraint, we can use a boolean array `cols` of length $n$ to record whether each column has a queen. Before each placement decision, we use `cols` to prune columns that already have queens, and dynamically update the state of `cols` during backtracking.
|
||||
|
||||
!!! tip
|
||||
|
||||
Note that the origin of the matrix is located in the upper left corner, where the row index increases from top to bottom, and the column index increases from left to right.
|
||||
Please note that the origin of the matrix is located in the upper-left corner, where the row index increases from top to bottom, and the column index increases from left to right.
|
||||
|
||||
How about the diagonal constraints? Let the row and column indices of a certain cell on the chessboard be $(row, col)$. By selecting a specific main diagonal, we notice that the difference $row - col$ is the same for all cells on that diagonal, **meaning that $row - col$ is a constant value on the main diagonal**.
|
||||
So how do we handle diagonal constraints? Consider a square on the chessboard with row and column indices $(row, col)$. If we select a specific main diagonal in the matrix, we find that all squares on that diagonal have the same difference between their row and column indices, **meaning that $row - col$ is a constant value for all squares on the main diagonal**.
|
||||
|
||||
In other words, if two cells satisfy $row_1 - col_1 = row_2 - col_2$, they are definitely on the same main diagonal. Using this pattern, we can utilize the array `diags1` shown in Figure 13-18 to track whether a queen is on any main diagonal.
|
||||
In other words, if two squares satisfy $row_1 - col_1 = row_2 - col_2$, they must be on the same main diagonal. Using this pattern, we can use the array `diags1` shown in Figure 13-18 to record whether there is a queen on each main diagonal.
|
||||
|
||||
Similarly, **the sum of $row + col$ is a constant value for all cells on the secondary diagonal**. We can also use the array `diags2` to handle secondary diagonal constraints.
|
||||
Similarly, **for all squares on an anti-diagonal, the sum $row + col$ is a constant value**. We can likewise use the array `diags2` to handle anti-diagonal constraints.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Figure 13-18 Handling column and diagonal constraints </p>
|
||||
|
||||
### 3. Code implementation
|
||||
### 3. Code Implementation
|
||||
|
||||
Please note, in an $n$-dimensional square matrix, the range of $row - col$ is $[-n + 1, n - 1]$, and the range of $row + col$ is $[0, 2n - 2]$. Consequently, the number of both main and secondary diagonals is $2n - 1$, meaning the length of the arrays `diags1` and `diags2` is $2n - 1$.
|
||||
Please note that in an $n$-dimensional square matrix, the range of $row - col$ is $[-n + 1, n - 1]$, and the range of $row + col$ is $[0, 2n - 2]$. Therefore, the number of both main diagonals and anti-diagonals is $2n - 1$, meaning the length of both arrays `diags1` and `diags2` is $2n - 1$.
|
||||
|
||||
=== "Python"
|
||||
|
||||
@@ -68,34 +68,34 @@ Please note, in an $n$-dimensional square matrix, the range of $row - col$ is $[
|
||||
diags1: list[bool],
|
||||
diags2: list[bool],
|
||||
):
|
||||
"""Backtracking algorithm: n queens"""
|
||||
"""Backtracking algorithm: N queens"""
|
||||
# When all rows are placed, record the solution
|
||||
if row == n:
|
||||
res.append([list(row) for row in state])
|
||||
return
|
||||
# Traverse all columns
|
||||
for col in range(n):
|
||||
# Calculate the main and minor diagonals corresponding to the cell
|
||||
# Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
diag1 = row - col + n - 1
|
||||
diag2 = row + col
|
||||
# Pruning: do not allow queens on the column, main diagonal, or minor diagonal of the cell
|
||||
# Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if not cols[col] and not diags1[diag1] and not diags2[diag2]:
|
||||
# Attempt: place the queen in the cell
|
||||
# Attempt: place the queen in this cell
|
||||
state[row][col] = "Q"
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = True
|
||||
# Place the next row
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2)
|
||||
# Retract: restore the cell to an empty spot
|
||||
# Backtrack: restore this cell to an empty cell
|
||||
state[row][col] = "#"
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = False
|
||||
|
||||
def n_queens(n: int) -> list[list[list[str]]]:
|
||||
"""Solve n queens"""
|
||||
# Initialize an n*n size chessboard, where 'Q' represents the queen and '#' represents an empty spot
|
||||
"""Solve N queens"""
|
||||
# Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
state = [["#" for _ in range(n)] for _ in range(n)]
|
||||
cols = [False] * n # Record columns with queens
|
||||
diags1 = [False] * (2 * n - 1) # Record main diagonals with queens
|
||||
diags2 = [False] * (2 * n - 1) # Record minor diagonals with queens
|
||||
cols = [False] * n # Record whether there is a queen in the column
|
||||
diags1 = [False] * (2 * n - 1) # Record whether there is a queen on the main diagonal
|
||||
diags2 = [False] * (2 * n - 1) # Record whether there is a queen on the anti-diagonal
|
||||
res = []
|
||||
backtrack(0, n, state, res, cols, diags1, diags2)
|
||||
|
||||
@@ -105,7 +105,7 @@ Please note, in an $n$-dimensional square matrix, the range of $row - col$ is $[
|
||||
=== "C++"
|
||||
|
||||
```cpp title="n_queens.cpp"
|
||||
/* Backtracking algorithm: n queens */
|
||||
/* Backtracking algorithm: N queens */
|
||||
void backtrack(int row, int n, vector<vector<string>> &state, vector<vector<vector<string>>> &res, vector<bool> &cols,
|
||||
vector<bool> &diags1, vector<bool> &diags2) {
|
||||
// When all rows are placed, record the solution
|
||||
@@ -115,30 +115,30 @@ Please note, in an $n$-dimensional square matrix, the range of $row - col$ is $[
|
||||
}
|
||||
// Traverse all columns
|
||||
for (int col = 0; col < n; col++) {
|
||||
// Calculate the main and minor diagonals corresponding to the cell
|
||||
// Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
int diag1 = row - col + n - 1;
|
||||
int diag2 = row + col;
|
||||
// Pruning: do not allow queens on the column, main diagonal, or minor diagonal of the cell
|
||||
// Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
|
||||
// Attempt: place the queen in the cell
|
||||
// Attempt: place the queen in this cell
|
||||
state[row][col] = "Q";
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = true;
|
||||
// Place the next row
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2);
|
||||
// Retract: restore the cell to an empty spot
|
||||
// Backtrack: restore this cell to an empty cell
|
||||
state[row][col] = "#";
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Solve n queens */
|
||||
/* Solve N queens */
|
||||
vector<vector<vector<string>>> nQueens(int n) {
|
||||
// Initialize an n*n size chessboard, where 'Q' represents the queen and '#' represents an empty spot
|
||||
// Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
vector<vector<string>> state(n, vector<string>(n, "#"));
|
||||
vector<bool> cols(n, false); // Record columns with queens
|
||||
vector<bool> diags1(2 * n - 1, false); // Record main diagonals with queens
|
||||
vector<bool> diags2(2 * n - 1, false); // Record minor diagonals with queens
|
||||
vector<bool> cols(n, false); // Record whether there is a queen in the column
|
||||
vector<bool> diags1(2 * n - 1, false); // Record whether there is a queen on the main diagonal
|
||||
vector<bool> diags2(2 * n - 1, false); // Record whether there is a queen on the anti-diagonal
|
||||
vector<vector<vector<string>>> res;
|
||||
|
||||
backtrack(0, n, state, res, cols, diags1, diags2);
|
||||
@@ -150,7 +150,7 @@ Please note, in an $n$-dimensional square matrix, the range of $row - col$ is $[
|
||||
=== "Java"
|
||||
|
||||
```java title="n_queens.java"
|
||||
/* Backtracking algorithm: n queens */
|
||||
/* Backtracking algorithm: N queens */
|
||||
void backtrack(int row, int n, List<List<String>> state, List<List<List<String>>> res,
|
||||
boolean[] cols, boolean[] diags1, boolean[] diags2) {
|
||||
// When all rows are placed, record the solution
|
||||
@@ -164,26 +164,26 @@ Please note, in an $n$-dimensional square matrix, the range of $row - col$ is $[
|
||||
}
|
||||
// Traverse all columns
|
||||
for (int col = 0; col < n; col++) {
|
||||
// Calculate the main and minor diagonals corresponding to the cell
|
||||
// Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
int diag1 = row - col + n - 1;
|
||||
int diag2 = row + col;
|
||||
// Pruning: do not allow queens on the column, main diagonal, or minor diagonal of the cell
|
||||
// Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
|
||||
// Attempt: place the queen in the cell
|
||||
// Attempt: place the queen in this cell
|
||||
state.get(row).set(col, "Q");
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = true;
|
||||
// Place the next row
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2);
|
||||
// Retract: restore the cell to an empty spot
|
||||
// Backtrack: restore this cell to an empty cell
|
||||
state.get(row).set(col, "#");
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Solve n queens */
|
||||
/* Solve N queens */
|
||||
List<List<List<String>>> nQueens(int n) {
|
||||
// Initialize an n*n size chessboard, where 'Q' represents the queen and '#' represents an empty spot
|
||||
// Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
List<List<String>> state = new ArrayList<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
List<String> row = new ArrayList<>();
|
||||
@@ -192,9 +192,9 @@ Please note, in an $n$-dimensional square matrix, the range of $row - col$ is $[
|
||||
}
|
||||
state.add(row);
|
||||
}
|
||||
boolean[] cols = new boolean[n]; // Record columns with queens
|
||||
boolean[] diags1 = new boolean[2 * n - 1]; // Record main diagonals with queens
|
||||
boolean[] diags2 = new boolean[2 * n - 1]; // Record minor diagonals with queens
|
||||
boolean[] cols = new boolean[n]; // Record whether there is a queen in the column
|
||||
boolean[] diags1 = new boolean[2 * n - 1]; // Record whether there is a queen on the main diagonal
|
||||
boolean[] diags2 = new boolean[2 * n - 1]; // Record whether there is a queen on the anti-diagonal
|
||||
List<List<List<String>>> res = new ArrayList<>();
|
||||
|
||||
backtrack(0, n, state, res, cols, diags1, diags2);
|
||||
@@ -206,91 +206,544 @@ Please note, in an $n$-dimensional square matrix, the range of $row - col$ is $[
|
||||
=== "C#"
|
||||
|
||||
```csharp title="n_queens.cs"
|
||||
[class]{n_queens}-[func]{Backtrack}
|
||||
/* Backtracking algorithm: N queens */
|
||||
void Backtrack(int row, int n, List<List<string>> state, List<List<List<string>>> res,
|
||||
bool[] cols, bool[] diags1, bool[] diags2) {
|
||||
// When all rows are placed, record the solution
|
||||
if (row == n) {
|
||||
List<List<string>> copyState = [];
|
||||
foreach (List<string> sRow in state) {
|
||||
copyState.Add(new List<string>(sRow));
|
||||
}
|
||||
res.Add(copyState);
|
||||
return;
|
||||
}
|
||||
// Traverse all columns
|
||||
for (int col = 0; col < n; col++) {
|
||||
// Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
int diag1 = row - col + n - 1;
|
||||
int diag2 = row + col;
|
||||
// Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
|
||||
// Attempt: place the queen in this cell
|
||||
state[row][col] = "Q";
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = true;
|
||||
// Place the next row
|
||||
Backtrack(row + 1, n, state, res, cols, diags1, diags2);
|
||||
// Backtrack: restore this cell to an empty cell
|
||||
state[row][col] = "#";
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[class]{n_queens}-[func]{NQueens}
|
||||
/* Solve N queens */
|
||||
List<List<List<string>>> NQueens(int n) {
|
||||
// Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
List<List<string>> state = [];
|
||||
for (int i = 0; i < n; i++) {
|
||||
List<string> row = [];
|
||||
for (int j = 0; j < n; j++) {
|
||||
row.Add("#");
|
||||
}
|
||||
state.Add(row);
|
||||
}
|
||||
bool[] cols = new bool[n]; // Record whether there is a queen in the column
|
||||
bool[] diags1 = new bool[2 * n - 1]; // Record whether there is a queen on the main diagonal
|
||||
bool[] diags2 = new bool[2 * n - 1]; // Record whether there is a queen on the anti-diagonal
|
||||
List<List<List<string>>> res = [];
|
||||
|
||||
Backtrack(0, n, state, res, cols, diags1, diags2);
|
||||
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="n_queens.go"
|
||||
[class]{}-[func]{backtrack}
|
||||
/* Backtracking algorithm: N queens */
|
||||
func backtrack(row, n int, state *[][]string, res *[][][]string, cols, diags1, diags2 *[]bool) {
|
||||
// When all rows are placed, record the solution
|
||||
if row == n {
|
||||
newState := make([][]string, len(*state))
|
||||
for i, _ := range newState {
|
||||
newState[i] = make([]string, len((*state)[0]))
|
||||
copy(newState[i], (*state)[i])
|
||||
|
||||
[class]{}-[func]{nQueens}
|
||||
}
|
||||
*res = append(*res, newState)
|
||||
return
|
||||
}
|
||||
// Traverse all columns
|
||||
for col := 0; col < n; col++ {
|
||||
// Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
diag1 := row - col + n - 1
|
||||
diag2 := row + col
|
||||
// Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if !(*cols)[col] && !(*diags1)[diag1] && !(*diags2)[diag2] {
|
||||
// Attempt: place the queen in this cell
|
||||
(*state)[row][col] = "Q"
|
||||
(*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = true, true, true
|
||||
// Place the next row
|
||||
backtrack(row+1, n, state, res, cols, diags1, diags2)
|
||||
// Backtrack: restore this cell to an empty cell
|
||||
(*state)[row][col] = "#"
|
||||
(*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = false, false, false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Solve N queens */
|
||||
func nQueens(n int) [][][]string {
|
||||
// Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
state := make([][]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
row := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
row[i] = "#"
|
||||
}
|
||||
state[i] = row
|
||||
}
|
||||
// Record whether there is a queen in the column
|
||||
cols := make([]bool, n)
|
||||
diags1 := make([]bool, 2*n-1)
|
||||
diags2 := make([]bool, 2*n-1)
|
||||
res := make([][][]string, 0)
|
||||
backtrack(0, n, &state, &res, &cols, &diags1, &diags2)
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="n_queens.swift"
|
||||
[class]{}-[func]{backtrack}
|
||||
/* Backtracking algorithm: N queens */
|
||||
func backtrack(row: Int, n: Int, state: inout [[String]], res: inout [[[String]]], cols: inout [Bool], diags1: inout [Bool], diags2: inout [Bool]) {
|
||||
// When all rows are placed, record the solution
|
||||
if row == n {
|
||||
res.append(state)
|
||||
return
|
||||
}
|
||||
// Traverse all columns
|
||||
for col in 0 ..< n {
|
||||
// Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
let diag1 = row - col + n - 1
|
||||
let diag2 = row + col
|
||||
// Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if !cols[col] && !diags1[diag1] && !diags2[diag2] {
|
||||
// Attempt: place the queen in this cell
|
||||
state[row][col] = "Q"
|
||||
cols[col] = true
|
||||
diags1[diag1] = true
|
||||
diags2[diag2] = true
|
||||
// Place the next row
|
||||
backtrack(row: row + 1, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2)
|
||||
// Backtrack: restore this cell to an empty cell
|
||||
state[row][col] = "#"
|
||||
cols[col] = false
|
||||
diags1[diag1] = false
|
||||
diags2[diag2] = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[class]{}-[func]{nQueens}
|
||||
/* Solve N queens */
|
||||
func nQueens(n: Int) -> [[[String]]] {
|
||||
// Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
var state = Array(repeating: Array(repeating: "#", count: n), count: n)
|
||||
var cols = Array(repeating: false, count: n) // Record whether there is a queen in the column
|
||||
var diags1 = Array(repeating: false, count: 2 * n - 1) // Record whether there is a queen on the main diagonal
|
||||
var diags2 = Array(repeating: false, count: 2 * n - 1) // Record whether there is a queen on the anti-diagonal
|
||||
var res: [[[String]]] = []
|
||||
|
||||
backtrack(row: 0, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2)
|
||||
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="n_queens.js"
|
||||
[class]{}-[func]{backtrack}
|
||||
/* Backtracking algorithm: N queens */
|
||||
function backtrack(row, n, state, res, cols, diags1, diags2) {
|
||||
// When all rows are placed, record the solution
|
||||
if (row === n) {
|
||||
res.push(state.map((row) => row.slice()));
|
||||
return;
|
||||
}
|
||||
// Traverse all columns
|
||||
for (let col = 0; col < n; col++) {
|
||||
// Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
const diag1 = row - col + n - 1;
|
||||
const diag2 = row + col;
|
||||
// Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
|
||||
// Attempt: place the queen in this cell
|
||||
state[row][col] = 'Q';
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = true;
|
||||
// Place the next row
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2);
|
||||
// Backtrack: restore this cell to an empty cell
|
||||
state[row][col] = '#';
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[class]{}-[func]{nQueens}
|
||||
/* Solve N queens */
|
||||
function nQueens(n) {
|
||||
// Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
const state = Array.from({ length: n }, () => Array(n).fill('#'));
|
||||
const cols = Array(n).fill(false); // Record whether there is a queen in the column
|
||||
const diags1 = Array(2 * n - 1).fill(false); // Record whether there is a queen on the main diagonal
|
||||
const diags2 = Array(2 * n - 1).fill(false); // Record whether there is a queen on the anti-diagonal
|
||||
const res = [];
|
||||
|
||||
backtrack(0, n, state, res, cols, diags1, diags2);
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="n_queens.ts"
|
||||
[class]{}-[func]{backtrack}
|
||||
/* Backtracking algorithm: N queens */
|
||||
function backtrack(
|
||||
row: number,
|
||||
n: number,
|
||||
state: string[][],
|
||||
res: string[][][],
|
||||
cols: boolean[],
|
||||
diags1: boolean[],
|
||||
diags2: boolean[]
|
||||
): void {
|
||||
// When all rows are placed, record the solution
|
||||
if (row === n) {
|
||||
res.push(state.map((row) => row.slice()));
|
||||
return;
|
||||
}
|
||||
// Traverse all columns
|
||||
for (let col = 0; col < n; col++) {
|
||||
// Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
const diag1 = row - col + n - 1;
|
||||
const diag2 = row + col;
|
||||
// Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
|
||||
// Attempt: place the queen in this cell
|
||||
state[row][col] = 'Q';
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = true;
|
||||
// Place the next row
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2);
|
||||
// Backtrack: restore this cell to an empty cell
|
||||
state[row][col] = '#';
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[class]{}-[func]{nQueens}
|
||||
/* Solve N queens */
|
||||
function nQueens(n: number): string[][][] {
|
||||
// Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
const state = Array.from({ length: n }, () => Array(n).fill('#'));
|
||||
const cols = Array(n).fill(false); // Record whether there is a queen in the column
|
||||
const diags1 = Array(2 * n - 1).fill(false); // Record whether there is a queen on the main diagonal
|
||||
const diags2 = Array(2 * n - 1).fill(false); // Record whether there is a queen on the anti-diagonal
|
||||
const res: string[][][] = [];
|
||||
|
||||
backtrack(0, n, state, res, cols, diags1, diags2);
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="n_queens.dart"
|
||||
[class]{}-[func]{backtrack}
|
||||
/* Backtracking algorithm: N queens */
|
||||
void backtrack(
|
||||
int row,
|
||||
int n,
|
||||
List<List<String>> state,
|
||||
List<List<List<String>>> res,
|
||||
List<bool> cols,
|
||||
List<bool> diags1,
|
||||
List<bool> diags2,
|
||||
) {
|
||||
// When all rows are placed, record the solution
|
||||
if (row == n) {
|
||||
List<List<String>> copyState = [];
|
||||
for (List<String> sRow in state) {
|
||||
copyState.add(List.from(sRow));
|
||||
}
|
||||
res.add(copyState);
|
||||
return;
|
||||
}
|
||||
// Traverse all columns
|
||||
for (int col = 0; col < n; col++) {
|
||||
// Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
int diag1 = row - col + n - 1;
|
||||
int diag2 = row + col;
|
||||
// Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
|
||||
// Attempt: place the queen in this cell
|
||||
state[row][col] = "Q";
|
||||
cols[col] = true;
|
||||
diags1[diag1] = true;
|
||||
diags2[diag2] = true;
|
||||
// Place the next row
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2);
|
||||
// Backtrack: restore this cell to an empty cell
|
||||
state[row][col] = "#";
|
||||
cols[col] = false;
|
||||
diags1[diag1] = false;
|
||||
diags2[diag2] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[class]{}-[func]{nQueens}
|
||||
/* Solve N queens */
|
||||
List<List<List<String>>> nQueens(int n) {
|
||||
// Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
List<List<String>> state = List.generate(n, (index) => List.filled(n, "#"));
|
||||
List<bool> cols = List.filled(n, false); // Record whether there is a queen in the column
|
||||
List<bool> diags1 = List.filled(2 * n - 1, false); // Record whether there is a queen on the main diagonal
|
||||
List<bool> diags2 = List.filled(2 * n - 1, false); // Record whether there is a queen on the anti-diagonal
|
||||
List<List<List<String>>> res = [];
|
||||
|
||||
backtrack(0, n, state, res, cols, diags1, diags2);
|
||||
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="n_queens.rs"
|
||||
[class]{}-[func]{backtrack}
|
||||
/* Backtracking algorithm: N queens */
|
||||
fn backtrack(
|
||||
row: usize,
|
||||
n: usize,
|
||||
state: &mut Vec<Vec<String>>,
|
||||
res: &mut Vec<Vec<Vec<String>>>,
|
||||
cols: &mut [bool],
|
||||
diags1: &mut [bool],
|
||||
diags2: &mut [bool],
|
||||
) {
|
||||
// When all rows are placed, record the solution
|
||||
if row == n {
|
||||
res.push(state.clone());
|
||||
return;
|
||||
}
|
||||
// Traverse all columns
|
||||
for col in 0..n {
|
||||
// Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
let diag1 = row + n - 1 - col;
|
||||
let diag2 = row + col;
|
||||
// Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if !cols[col] && !diags1[diag1] && !diags2[diag2] {
|
||||
// Attempt: place the queen in this cell
|
||||
state[row][col] = "Q".into();
|
||||
(cols[col], diags1[diag1], diags2[diag2]) = (true, true, true);
|
||||
// Place the next row
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2);
|
||||
// Backtrack: restore this cell to an empty cell
|
||||
state[row][col] = "#".into();
|
||||
(cols[col], diags1[diag1], diags2[diag2]) = (false, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[class]{}-[func]{n_queens}
|
||||
/* Solve N queens */
|
||||
fn n_queens(n: usize) -> Vec<Vec<Vec<String>>> {
|
||||
// Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
let mut state: Vec<Vec<String>> = vec![vec!["#".to_string(); n]; n];
|
||||
let mut cols = vec![false; n]; // Record whether there is a queen in the column
|
||||
let mut diags1 = vec![false; 2 * n - 1]; // Record whether there is a queen on the main diagonal
|
||||
let mut diags2 = vec![false; 2 * n - 1]; // Record whether there is a queen on the anti-diagonal
|
||||
let mut res: Vec<Vec<Vec<String>>> = Vec::new();
|
||||
|
||||
backtrack(
|
||||
0,
|
||||
n,
|
||||
&mut state,
|
||||
&mut res,
|
||||
&mut cols,
|
||||
&mut diags1,
|
||||
&mut diags2,
|
||||
);
|
||||
|
||||
res
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="n_queens.c"
|
||||
[class]{}-[func]{backtrack}
|
||||
/* Backtracking algorithm: N queens */
|
||||
void backtrack(int row, int n, char state[MAX_SIZE][MAX_SIZE], char ***res, int *resSize, bool cols[MAX_SIZE],
|
||||
bool diags1[2 * MAX_SIZE - 1], bool diags2[2 * MAX_SIZE - 1]) {
|
||||
// When all rows are placed, record the solution
|
||||
if (row == n) {
|
||||
res[*resSize] = (char **)malloc(sizeof(char *) * n);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
res[*resSize][i] = (char *)malloc(sizeof(char) * (n + 1));
|
||||
strcpy(res[*resSize][i], state[i]);
|
||||
}
|
||||
(*resSize)++;
|
||||
return;
|
||||
}
|
||||
// Traverse all columns
|
||||
for (int col = 0; col < n; col++) {
|
||||
// Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
int diag1 = row - col + n - 1;
|
||||
int diag2 = row + col;
|
||||
// Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
|
||||
// Attempt: place the queen in this cell
|
||||
state[row][col] = 'Q';
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = true;
|
||||
// Place the next row
|
||||
backtrack(row + 1, n, state, res, resSize, cols, diags1, diags2);
|
||||
// Backtrack: restore this cell to an empty cell
|
||||
state[row][col] = '#';
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[class]{}-[func]{nQueens}
|
||||
/* Solve N queens */
|
||||
char ***nQueens(int n, int *returnSize) {
|
||||
char state[MAX_SIZE][MAX_SIZE];
|
||||
// Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
for (int i = 0; i < n; ++i) {
|
||||
for (int j = 0; j < n; ++j) {
|
||||
state[i][j] = '#';
|
||||
}
|
||||
state[i][n] = '\0';
|
||||
}
|
||||
bool cols[MAX_SIZE] = {false}; // Record whether there is a queen in the column
|
||||
bool diags1[2 * MAX_SIZE - 1] = {false}; // Record whether there is a queen on the main diagonal
|
||||
bool diags2[2 * MAX_SIZE - 1] = {false}; // Record whether there is a queen on the anti-diagonal
|
||||
|
||||
char ***res = (char ***)malloc(sizeof(char **) * MAX_SIZE);
|
||||
*returnSize = 0;
|
||||
backtrack(0, n, state, res, returnSize, cols, diags1, diags2);
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="n_queens.kt"
|
||||
[class]{}-[func]{backtrack}
|
||||
/* Backtracking algorithm: N queens */
|
||||
fun backtrack(
|
||||
row: Int,
|
||||
n: Int,
|
||||
state: MutableList<MutableList<String>>,
|
||||
res: MutableList<MutableList<MutableList<String>>?>,
|
||||
cols: BooleanArray,
|
||||
diags1: BooleanArray,
|
||||
diags2: BooleanArray
|
||||
) {
|
||||
// When all rows are placed, record the solution
|
||||
if (row == n) {
|
||||
val copyState = mutableListOf<MutableList<String>>()
|
||||
for (sRow in state) {
|
||||
copyState.add(sRow.toMutableList())
|
||||
}
|
||||
res.add(copyState)
|
||||
return
|
||||
}
|
||||
// Traverse all columns
|
||||
for (col in 0..<n) {
|
||||
// Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
val diag1 = row - col + n - 1
|
||||
val diag2 = row + col
|
||||
// Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
|
||||
// Attempt: place the queen in this cell
|
||||
state[row][col] = "Q"
|
||||
diags2[diag2] = true
|
||||
diags1[diag1] = diags2[diag2]
|
||||
cols[col] = diags1[diag1]
|
||||
// Place the next row
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2)
|
||||
// Backtrack: restore this cell to an empty cell
|
||||
state[row][col] = "#"
|
||||
diags2[diag2] = false
|
||||
diags1[diag1] = diags2[diag2]
|
||||
cols[col] = diags1[diag1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[class]{}-[func]{nQueens}
|
||||
/* Solve N queens */
|
||||
fun nQueens(n: Int): MutableList<MutableList<MutableList<String>>?> {
|
||||
// Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
val state = mutableListOf<MutableList<String>>()
|
||||
for (i in 0..<n) {
|
||||
val row = mutableListOf<String>()
|
||||
for (j in 0..<n) {
|
||||
row.add("#")
|
||||
}
|
||||
state.add(row)
|
||||
}
|
||||
val cols = BooleanArray(n) // Record whether there is a queen in the column
|
||||
val diags1 = BooleanArray(2 * n - 1) // Record whether there is a queen on the main diagonal
|
||||
val diags2 = BooleanArray(2 * n - 1) // Record whether there is a queen on the anti-diagonal
|
||||
val res = mutableListOf<MutableList<MutableList<String>>?>()
|
||||
|
||||
backtrack(0, n, state, res, cols, diags1, diags2)
|
||||
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="n_queens.rb"
|
||||
[class]{}-[func]{backtrack}
|
||||
### Backtracking: n queens ###
|
||||
def backtrack(row, n, state, res, cols, diags1, diags2)
|
||||
# When all rows are placed, record the solution
|
||||
if row == n
|
||||
res << state.map { |row| row.dup }
|
||||
return
|
||||
end
|
||||
|
||||
[class]{}-[func]{n_queens}
|
||||
# Traverse all columns
|
||||
for col in 0...n
|
||||
# Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
diag1 = row - col + n - 1
|
||||
diag2 = row + col
|
||||
# Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if !cols[col] && !diags1[diag1] && !diags2[diag2]
|
||||
# Attempt: place the queen in this cell
|
||||
state[row][col] = "Q"
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = true
|
||||
# Place the next row
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2)
|
||||
# Backtrack: restore this cell to an empty cell
|
||||
state[row][col] = "#"
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
### Solve n queens ###
|
||||
def n_queens(n)
|
||||
# Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
state = Array.new(n) { Array.new(n, "#") }
|
||||
cols = Array.new(n, false) # Record whether there is a queen in the column
|
||||
diags1 = Array.new(2 * n - 1, false) # Record whether there is a queen on the main diagonal
|
||||
diags2 = Array.new(2 * n - 1, false) # Record whether there is a queen on the anti-diagonal
|
||||
res = []
|
||||
backtrack(0, n, state, res, cols, diags1, diags2)
|
||||
|
||||
res
|
||||
end
|
||||
```
|
||||
|
||||
=== "Zig"
|
||||
Placing $n$ queens row by row, considering the column constraint, from the first row to the last row there are $n$, $n-1$, $\dots$, $2$, $1$ choices, using $O(n!)$ time. When recording a solution, it is necessary to copy the matrix `state` and add it to `res`, and the copy operation uses $O(n^2)$ time. Therefore, **the overall time complexity is $O(n! \cdot n^2)$**. In practice, pruning based on diagonal constraints can also significantly reduce the search space, so the search efficiency is often better than the time complexity mentioned above.
|
||||
|
||||
```zig title="n_queens.zig"
|
||||
[class]{}-[func]{backtrack}
|
||||
|
||||
[class]{}-[func]{nQueens}
|
||||
```
|
||||
|
||||
Placing $n$ queens row-by-row, considering column constraints, from the first row to the last row, there are $n$, $n-1$, $\dots$, $2$, $1$ choices, using $O(n!)$ time. When recording a solution, it is necessary to copy the matrix `state` and add it to `res`, with the copying operation using $O(n^2)$ time. Therefore, **the overall time complexity is $O(n! \cdot n^2)$**. In practice, pruning based on diagonal constraints can significantly reduce the search space, thus often the search efficiency is better than the aforementioned time complexity.
|
||||
|
||||
Array `state` uses $O(n^2)$ space, and arrays `cols`, `diags1`, and `diags2` each use $O(n)$ space as well. The maximum recursion depth is $n$, using $O(n)$ stack frame space. Therefore, **the space complexity is $O(n^2)$**.
|
||||
The array `state` uses $O(n^2)$ space, and the arrays `cols`, `diags1`, and `diags2` each use $O(n)$ space. The maximum recursion depth is $n$, using $O(n)$ stack frame space. Therefore, **the space complexity is $O(n^2)$**.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,24 +4,24 @@ comments: true
|
||||
|
||||
# 13.5 Summary
|
||||
|
||||
### 1. Key review
|
||||
### 1. Key Review
|
||||
|
||||
- The essence of the backtracking algorithm is exhaustive search. It seeks solutions that meet the conditions by performing a depth-first traversal of the solution space. During the search, if a satisfying solution is found, it is recorded, until all solutions are found or the traversal is completed.
|
||||
- The search process of the backtracking algorithm includes trying and backtracking. It uses depth-first search to explore various choices, and when a choice does not meet the constraints, the previous choice is undone. Then it reverts to the previous state and continues to try other options. Trying and backtracking are operations in opposite directions.
|
||||
- Backtracking problems usually contain multiple constraints. These constraints can be used to perform pruning operations. Pruning can terminate unnecessary search branches in advance, greatly enhancing search efficiency.
|
||||
- The backtracking algorithm is mainly used to solve search problems and constraint satisfaction problems. Although combinatorial optimization problems can be solved using backtracking, there are often more efficient or effective solutions available.
|
||||
- The permutation problem aims to search for all possible permutations of the elements in a given set. We use an array to record whether each element has been chosen, avoiding repeated selection of the same element. This ensures that each element is chosen only once.
|
||||
- In permutation problems, if the set contains duplicate elements, the final result will include duplicate permutations. We need to restrict that identical elements can only be selected once in each round, which is usually implemented using a hash set.
|
||||
- The subset-sum problem aims to find all subsets in a given set that sum to a target value. The set does not distinguish the order of elements, but the search process may generate duplicate subsets. This occurs because the algorithm explores different element orders as unique paths. Before backtracking, we sort the data and set a variable to indicate the starting point of the traversal for each round. This allows us to prune the search branches that generate duplicate subsets.
|
||||
- For the subset-sum problem, equal elements in the array can produce duplicate sets. Using the precondition that the array is already sorted, we prune by determining if adjacent elements are equal. This ensures that equal elements are only selected once per round.
|
||||
- The $n$ queens problem aims to find schemes to place $n$ queens on an $n \times n$ chessboard such that no two queens can attack each other. The constraints of the problem include row constraints, column constraints, and constraints on the main and secondary diagonals. To meet the row constraint, we adopt a strategy of placing one queen per row, ensuring each row has one queen placed.
|
||||
- The handling of column constraints and diagonal constraints is similar. For column constraints, we use an array to record whether there is a queen in each column, thereby indicating whether the selected cell is legal. For diagonal constraints, we use two arrays to respectively record the presence of queens on the main and secondary diagonals. The challenge is to determine the relationship between row and column indices for cells on the same main or secondary diagonal.
|
||||
- The backtracking algorithm is fundamentally an exhaustive search method. It finds solutions that meet specified conditions by performing a depth-first traversal of the solution space. During the search process, when a solution satisfying the conditions is found, it is recorded. The search ends either after finding all solutions or when the traversal is complete.
|
||||
- The backtracking algorithm search process consists of two parts: attempting and backtracking. It tries various choices through depth-first search. When encountering situations that violate constraints, it reverts the previous choice, returns to the previous state, and continues exploring other options. Attempting and backtracking are operations in opposite directions.
|
||||
- Backtracking problems typically contain multiple constraints, which can be utilized to implement pruning operations. Pruning can terminate unnecessary search branches early, significantly improving search efficiency.
|
||||
- The backtracking algorithm is primarily used to solve search problems and constraint satisfaction problems. While combinatorial optimization problems can be solved with backtracking, there are often more efficient or better-performing solutions available.
|
||||
- The permutation problem aims to find all possible permutations of elements in a given set. We use an array to record whether each element has been selected, thereby pruning search branches that attempt to select the same element repeatedly, ensuring each element is selected exactly once.
|
||||
- In the permutation problem, if the set contains duplicate elements, the final result will contain duplicate permutations. We need to impose a constraint so that equal elements can only be selected once per round, which is typically achieved using a hash set.
|
||||
- The subset-sum problem aims to find all subsets of a given set that sum to a target value. Since the set is unordered but the search process outputs results in all orders, duplicate subsets are generated. We sort the data before backtracking and use a variable to indicate the starting point of each round's traversal, thereby pruning search branches that generate duplicate subsets.
|
||||
- For the subset-sum problem, equal elements in the array produce duplicate sets. We leverage the precondition that the array is sorted by checking whether adjacent elements are equal to implement pruning, ensuring that equal elements can only be selected once per round.
|
||||
- The $n$ queens problem aims to find placements of $n$ queens on an $n \times n$ chessboard such that no two queens can attack each other. The constraints of this problem include row constraints, column constraints, and main and anti-diagonal constraints. To satisfy row constraints, we adopt a row-by-row placement strategy, ensuring exactly one queen is placed in each row.
|
||||
- The handling of column constraints and diagonal constraints is similar. For column constraints, we use an array to record whether each column has a queen, thereby indicating whether a selected cell is valid. For diagonal constraints, we use two arrays to separately record whether queens exist on each main or anti-diagonal. The challenge lies in finding the row-column index pattern that characterizes cells on the same main (anti-)diagonal.
|
||||
|
||||
### 2. Q & A
|
||||
|
||||
**Q**: How can we understand the relationship between backtracking and recursion?
|
||||
**Q**: How should we understand the relationship between backtracking and recursion?
|
||||
|
||||
Overall, backtracking is an "algorithmic strategy," while recursion is more of a "tool."
|
||||
Overall, backtracking is an "algorithm strategy", while recursion is more like a "tool".
|
||||
|
||||
- Backtracking algorithms are typically based on recursion. However, backtracking is one of the application scenarios of recursion, specifically in search problems.
|
||||
- The structure of recursion reflects the problem-solving paradigm of "sub-problem decomposition." It is commonly used in solving problems involving divide and conquer, backtracking, and dynamic programming (memoized recursion).
|
||||
- The backtracking algorithm is typically implemented based on recursion. However, backtracking is one application scenario of recursion and represents the application of recursion in search problems.
|
||||
- The structure of recursion embodies the "subproblem decomposition" problem-solving paradigm, commonly used to solve problems involving divide-and-conquer, backtracking, and dynamic programming (memoized recursion).
|
||||
|
||||
Reference in New Issue
Block a user