N 皇后问题
「N 皇后问题」研究的是如何将 N 个皇后放置在 N ×N 的棋盘上,并且使皇后彼此之间不能相互攻击。皇后的走法是:可以横直斜走,格数不限。因此要求皇后彼此之间不能相互攻击,等价于要求任何两个皇后都不能在同一行、同一列以及同一条斜线上。
采用回溯法,记录上一步的位置,判断当前位置和上一位置是否在同一行、同一列或同一条斜线上。这一判断需要技巧。
初始位置:在第 row=0
行中选位置,每次递归 row+1
用 unordered_set<int> columns
记录以往位置的列标。
研究发现,同一条左斜线的行列索引和相等,同一右斜线的行列索引差相等,是每条斜线的特性。因此可以用 unordered_set<int> left_diag, right_diag
记录以往的位置信息。
class Solution {
public:
vector<vector<string>> results;
vector<int> track;
unordered_set<int> columns, left_diag, right_diag;
vector<vector<string>> solveNQueens(int n) {
trackback(0, n);
return results;
}
void trackback(int row, int n) {
if (row == n) {
auto r = createAnswer(track, n);
results.push_back(move(r));
} else {
for (int i = 0; i < n; i++) {
// 边界检查
if (columns.count(i)
|| left_diag.count(i + row)
|| right_diag.count(i - row))
continue;
// 做选择
track.push_back(i);
columns.insert(i);
left_diag.insert(i + row);
right_diag.insert(i - row);
// 前进
trackback(row + 1, n);
// 回溯(后退)
track.pop_back();
columns.erase(i);
left_diag.erase(i + row);
right_diag.erase(i - row);
}
}
}
vector<string> createAnswer(vector<int> &queens, int n) {
vector<string> ret(n, string(n, '.'));
for (int i = 0; i < n; i++) {
int index = queens[i];
ret[i][index] = 'Q';
}
return ret;
}
};
357. 计算各位数字互不相同的n位数有多少个
n = 0,有1个
n = 1,有10个
n = 2,有91个
这里不用排列组合的方法,只讨论回溯法。
路径中记录的类似这种 0 -> 0 -> 1 -> 2
也应该是符合条件的,需要特殊处理。
class Solution {
public:
vector<int> track;
int ret = 0;
int countNumbersWithUniqueDigits(int n) {
if (n == 0) return 1;
backtrack(0, n);
return ret;
}
void backtrack(int index, int n) {
if (index == n) {
ret++;
} else {
for (int i = 0; i < 10; i++) {
// 检查
if (find(track.begin(), track.end(), i) != track.end()) { // 数字重复了,非0数直接排除
if ((i == 0 && track.back() != 0) || i != 0) // 数字如果是0,且上一个数字不是0再排除
continue;
}
// 做选择
track.push_back(i);
// 前进
backtrack(index + 1, n);
// 撤回
track.pop_back();
}
}
}
};