已发布·持续改进
回溯法 指南 · 3/6
本章目前仅提供英文版。
This chapter implements subsets, permutations, and N-Queens in Python and walks through them line by line. Then it ports the same core routine, counting N-Queens solutions, to C++, Java, and TypeScript. Whatever the language, the structure is the same three lines: choose, explore, unchoose.
def subsets(nums):
result, path = [], []
def dfs(start):
result.append(path[:]) # every node is a subset
for i in range(start, len(nums)):
path.append(nums[i]) # choose
dfs(i + 1) # explore: only pick after i
path.pop() # unchoose
dfs(0)
return result
def permutations(nums):
result, path = [], []
used = [False] * len(nums)
def dfs():
if len(path) == len(nums): # leaf: one full permutation
result.append(path[:])
return
for i, x in enumerate(nums):
if used[i]: # skip elements already taken
continue
used[i] = True
path.append(x)
dfs()
path.pop()
used[i] = False
dfs()
return resultsubsets records a result at every node, not just at leaves. Because it only picks from start onward, [1, 2] and [2, 1] are never both produced.permutations cares about order, so it scans from the beginning every time and uses the used array to block elements already chosen. used[i] = False is the matching undo.path[:].def solve_n_queens(n):
cols, diag, anti = set(), set(), set()
queens, boards = [], []
def place(row):
if row == n: # every row filled
boards.append(["." * c + "Q" + "." * (n - c - 1) for c in queens])
return
for c in range(n):
if c in cols or row - c in diag or row + c in anti:
continue # prune
cols.add(c); diag.add(row - c); anti.add(row + c)
queens.append(c)
place(row + 1)
queens.pop()
cols.remove(c); diag.remove(row - c); anti.remove(row + c)
place(0)
return boards
for line in solve_n_queens(4)[0]:
print(line)
# .Q..
# ...Q
# Q...
# ..Q.row is the depth of the tree.cols, diag (row - c), and anti (row + c), make each conflict check O(1). Scanning every queen, as is_safe did in the previous chapter, costs per check.O(n)The other languages show the same routine, counting solutions only. Column and diagonal numbers are small integers, so boolean arrays replace the sets.
#include <iostream>
#include <vector>
int n;
std::vector<bool> col, diag, anti;
int place(int row) {
if (row == n) return 1;
int count = 0;
for (int c = 0; c < n; ++c) {
if (col[c] || diag[row - c + n - 1] || anti[row + c]) continue;
col[c] = diag[row - c + n - 1] = anti[row + c] = true;
count += place(row + 1);
col[c] = diag[row - c + n - 1] = anti[row + c] = false;
}
return count;
}
int main() {
n = 8;
col.assign(n, false);
diag.assign(2 * n - 1, false);
anti.assign(2 * n - 1, false);
std::cout << place(0) << "\n"; // 92
}Since row - c can be negative, adding n - 1 maps it onto array indices from 0 to 2n - 2.
public class NQueens {
static int n;
static boolean[] col, diag, anti;
static int place(int row) {
if (row == n) return 1;
int count = 0;
for (int c = 0; c < n; c++) {
if (col[c] || diag[row - c + n - 1] || anti[row + c]) continue;
col[c] = diag[row - c + n - 1] = anti[row + c] = true;
count += place(row + 1);
col[c] = diag[row - c + n - 1] = anti[row + c] = false;
}
return count;
}
public static void main(String[] args) {
n = 8;
col = new boolean[n];
diag = new boolean[2 * n - 1];
anti = new boolean[2 * n - 1];
System.out.println(place(0)); // 92
}
}function countNQueens(n: number): number {
const col = new Array<boolean>(n).fill(false);
const diag = new Array<boolean>(2 * n - 1).fill(false);
const anti = new Array<boolean>(2 * n - 1).fill(false);
const place = (row: number): number => {
if (row === n) return 1;
let count = 0;
for (let c = 0; c < n; c++) {
const d = row - c + n - 1;
if (col[c] || diag[d] || anti[row + c]) continue;
col[c] = diag[d] = anti[row + c] = true;
count += place(row + 1);
col[c] = diag[d] = anti[row + c] = false;
}
return count;
};
return place(0);
}
console.log(countNQueens(8)); // 92O(1).
0 条评论
登录 · 登录后即可发表评论。
来发表第一条评论吧。