N皇后问题(dfs回溯)
#include <bits/stdc++.h>
using namespace std;
const int N = 10;
int n, ans = 0;
bool a[N], b[2 * N], c[2 * N]; // 列、主对角线、副对角线占用情况
void f(int i) {
if (i == n+1) { // 成功放置 N 个皇后
ans++;
return;
}
for (int j = 1; j <= n; j++) { // 遍历当前行的所有列
if (!a[j] && !b[i - j + n] && !c[i + j]) { // 判断是否可放置
a[j] = b[i - j + n] = c[i + j] = true; // 标记占用
f(i + 1); // 递归尝试下一行
a[j] = b[i - j + n] = c[i + j] = false; // 回溯,恢复状态
}
}
}
int main() {
cin >> n;
f(1); // 从第 1 行开始放置皇后
cout << ans;
return 0;
}
每次放置时,检查当前放置的皇后是否会与已放置的皇后发生冲突(检查列、主对角线、副对角线)
如果合法,就继续尝试放置下一个皇后
如果到达了棋盘的最后一行,且所有皇后都成功放置,则记录一种有效的解
回溯“恢复状态”,撤销当前的选择,并尝试其他可能的选择
a[N]
: 用于标记每一列是否已被占用。
b[2 * N]
: 用于标记每一条主对角线是否已被占用。主对角线的编号是 i - j + n
,表示 (i, j)
位置上的皇后是否在该主对角线上。
c[2 * N]
: 用于标记每一条副对角线是否已被占用。副对角线的编号是 i + j
,表示 (i, j)
位置上的皇后是否在该副对角线上。
对于b[i - j + n]
:
i - j
:表示当前位置(i, j)
所在的主对角线的编号。比如,i - j == 0
表示该位置位于主对角线的中心(即(0,0)
到(n-1,n-1)
的对角线),i - j == -1
或i - j == 1
表示其他副对角线。为什么要+ n
:由于i - j
可能是负数(如i = 1
,j = 2
时i - j = -1
),我们将其偏移,使得所有的对角线编号都变为非负数。这样就能保证数组b
访问时不会越界。
对于c[i + j]
:
-
i + j
:表示当前位置(i, j)
所在的副对角线的编号。比如,i + j == 0
表示该位置位于副对角线的起点(即(0, n-1)
到(n-1, 0)
的对角线),i + j == 1
或i + j == n-1
表示其他副对角线。
通过这三个数组,可以有效地判断在放置皇后时,某个位置 (i, j)
是否会与其他皇后发生冲突,确保解法的有效性。
递归终止条件:当 i == n + 1
时,表示已经成功放置了 n
个皇后,此时我们增加答案 ans
,并返回。