前言
N皇后问题作为回溯法的经典题目,在算法界有着不可动摇的地位。如何优雅地通过回溯法枚举并筛选所有解?这篇博文不仅展示了代码,还有原理动画,助你彻底掌握~
一、问题描述
在n×n的棋盘上放n个皇后,使得任何两个皇后不处于同一行、同一列或同一对角线上(如图所示)。
二、算法建模
- 解向量:
x[i]
表示第i行皇后摆放在哪一列(每行恰好一个皇后) - 显约束:每个皇后必须在列1~n
- 隐约束:
- 不同行不同列
- 不同对角线:
abs(i−j)!=abs(x[i]−x[j])
三、核心代码实现
#include <stdio.h>
#include <math.h>
#define N 20 /* 最大皇后数 */
int n, sum;
int x[N+1]; /* x[i] 记录第i行皇后放在第几列 */
int Place(int k) {
int j;
for (j = 1; j < k; j++) {
if (abs(k - j) == abs(x[j] - x[k]) || x[j] == x[k])
return 0;
}
return 1;
}
void Backtrack(int t) {
int i;
if (t > n) {
sum++;
for (i = 1; i <= n; i++)
printf("%d ", x[i]);
printf("\n");
} else {
for (i = 1; i <= n; i++) {
x[t] = i;
if (Place(t))
Backtrack(t + 1);
}
}
}
int main(void) {
printf("请输入皇后数n:");
scanf("%d", &n);
sum = 0;
Backtrack(1);
printf("共有%d种解法\n", sum);
return 0;
}
解空间搜索树(4皇后问题):
由于对称性,省略放置(1,3),(1,4)。
棋盘解法(3皇后问题):
四、运行演示
以8皇后为例运行:
请输入皇后数n:8
1 5 8 6 3 7 2 4
1 6 8 3 7 4 2 5
1 7 4 6 8 2 5 3
1 7 5 8 2 4 6 3
2 4 6 8 3 1 7 5
2 5 7 1 3 8 6 4
2 5 7 4 1 8 6 3
2 6 1 7 4 8 3 5
2 6 8 3 1 4 7 5
2 7 3 6 8 5 1 4
2 7 5 8 1 4 6 3
2 8 6 1 3 5 7 4
3 1 7 5 8 2 4 6
3 5 2 8 1 7 4 6
3 5 2 8 6 4 7 1
3 5 7 1 4 2 8 6
3 5 8 4 1 7 2 6
3 6 2 5 8 1 7 4
3 6 2 7 1 4 8 5
3 6 2 7 5 1 8 4
3 6 4 1 8 5 7 2
3 6 4 2 8 5 7 1
3 6 8 1 4 7 5 2
3 6 8 1 5 7 2 4
3 6 8 2 4 1 7 5
3 7 2 8 5 1 4 6
3 7 2 8 6 4 1 5
3 8 4 7 1 6 2 5
4 1 5 8 2 7 3 6
4 1 5 8 6 3 7 2
4 2 5 8 6 1 3 7
4 2 7 3 6 8 1 5
4 2 7 3 6 8 5 1
4 2 7 5 1 8 6 3
4 2 8 5 7 1 3 6
4 2 8 6 1 3 5 7
4 6 1 5 2 8 3 7
4 6 8 2 7 1 3 5
4 6 8 3 1 7 5 2
4 7 1 8 5 2 6 3
4 7 3 8 2 5 1 6
4 7 5 2 6 1 3 8
4 7 5 3 1 6 8 2
4 8 1 3 6 2 7 5
4 8 1 5 7 2 6 3
4 8 5 3 1 7 2 6
5 1 4 6 8 2 7 3
5 1 8 4 2 7 3 6
5 1 8 6 3 7 2 4
5 2 4 6 8 3 1 7
5 2 4 7 3 8 6 1
5 2 6 1 7 4 8 3
5 2 8 1 4 7 3 6
5 3 1 6 8 2 4 7
5 3 1 7 2 8 6 4
5 3 8 4 7 1 6 2
5 7 1 3 8 6 4 2
5 7 1 4 2 8 6 3
5 7 2 4 8 1 3 6
5 7 2 6 3 1 4 8
5 7 2 6 3 1 8 4
5 7 4 1 3 8 6 2
5 8 4 1 3 6 2 7
5 8 4 1 7 2 6 3
6 1 5 2 8 3 7 4
6 2 7 1 3 5 8 4
6 2 7 1 4 8 5 3
6 3 1 7 5 8 2 4
6 3 1 8 4 2 7 5
6 3 1 8 5 2 4 7
6 3 5 7 1 4 2 8
6 3 5 8 1 4 2 7
6 3 7 2 4 8 1 5
6 3 7 2 8 5 1 4
6 3 7 4 1 8 2 5
6 4 1 5 8 2 7 3
6 4 2 8 5 7 1 3
6 4 7 1 3 5 2 8
6 4 7 1 8 2 5 3
6 8 2 4 1 7 5 3
7 1 3 8 6 4 2 5
7 2 4 1 8 5 3 6
7 2 6 3 1 4 8 5
7 3 1 6 8 5 2 4
7 3 8 2 5 1 6 4
7 4 2 5 8 1 3 6
7 4 2 8 6 1 3 5
7 5 3 1 6 8 2 4
8 2 4 1 7 5 3 6
8 2 5 3 1 7 4 6
8 3 1 6 2 5 7 4
8 4 1 3 6 2 7 5
共有92种解法
五、时间复杂度分析
每一行要放一个皇后,每个皇后有最多n个可选列,仅当不冲突时才递归下一层。
- 完全枚举:有n!种可能,因为每行1个,每列只放1个皇后。
- 剪枝优化:每次通过对角冲突/列冲突判断,显著剪去了大量无效分支,但最坏依然要枚举指数级解。
- 复杂度:最坏情况为O(n!),实际要远低于
,高效实现(比如位运算)可提升性能,但指数级别不可避免。
六、总结与扩展
- 本题核心:回溯剪枝。
- 启发式优化:可以用位运算、对称性减少重复。
- 面试、算法竞赛常客,熟练掌握必备!