探索经典算法问题的优雅解法,感受回溯算法的精妙之处!
N皇后问题要求在一个N×N的棋盘上放置N个皇后,使得它们互不攻击(即任意两个皇后不能处于同一行、同一列或同一对角线上)。这是一个经典的回溯算法应用场景,也是计算机科学中著名的组合优化问题。
回溯法采用"试错思想":尝试分步解决问题,当发现当前步骤不能得到有效解时,取消上一步甚至上几步的计算,再通过其他可能的分步继续寻找解。
int N; // 皇后数量(棋盘大小N×N)
int *queenPos; // 存储皇后位置,queenPos[i]表示第i行皇后所在的列
int solutionCount; // 解的数量统计
bool isSafe(int row, int col) {
for (int i = 0; i < row; i++) {
// 检查列冲突或对角线冲突
if (queenPos[i] == col ||
abs(row - i) == abs(col - queenPos[i])) {
return false;
}
}
return true;
}
该函数检查当前位置(row, col)
是否安全,通过:
|Δx| = |Δy|
特性)void backtrack(int row) {
if (row == N) { // 找到有效解
printSolution(); // 打印解法
solutionCount++;
return;
}
for (int col = 0; col < N; col++) {
if (isSafe(row, col)) {
queenPos[row] = col; // 放置皇后
backtrack(row + 1); // 递归处理下一行
}
}
}
该函数实现经典回溯:
int main() {
printf("请输入皇后数量 N: ");
scanf("%d", &N);
// 动态分配存储空间
queenPos = (int *)malloc(N * sizeof(int));
solutionCount = 0;
backtrack(0); // 从第0行开始搜索
printf("共有 %d 个解\n", solutionCount);
free(queenPos); // 释放内存
return 0;
}
请输入皇后数量 N: 4
解 1: (1, 2) (2, 4) (3, 1) (4, 3)
解 2: (1, 3) (2, 1) (3, 4) (4, 2)
共有 2 个解
请输入皇后数量 N: 8
解 1: (1, 1) (2, 5) (3, 8) ...
...
解 92: (1, 8) (2, 4) (3, 1) ...
共有 92 个解
该算法的时间复杂度为O(N!):
N皇后问题是理解回溯算法的绝佳案例:
“回溯算法如同人生探索:勇敢尝试,发现错误及时回头,最终定能找到属于自己的位置!”
附录:完整代码
#include
#include
#include
// 全局变量
int N; // 皇后数量(棋盘大小 N×N)
int *queenPos; // 存储皇后位置,queenPos[i] 表示第 i 行皇后所在的列
int solutionCount; // 解的数量
// 检查第 row 行第 col 列是否可以放置皇后
bool isSafe(int row, int col) {
for (int i = 0; i < row; i++) {
// 检查列冲突或对角线冲突
if (queenPos[i] == col || abs(row - i) == abs(col - queenPos[i])) {
return false;
}
}
return true;
}
// 打印当前解
void printSolution() {
printf("解 %d: ", solutionCount + 1);
for (int i = 0; i < N; i++) {
printf("(%d, %d) ", i + 1, queenPos[i] + 1);
}
printf("\n");
}
// 回溯函数:放置第 row 行的皇后
void backtrack(int row) {
// 递归终止条件:所有行都已放置皇后
if (row == N) {
printSolution();
solutionCount++;
return;
}
// 尝试在第 row 行的每一列放置皇后
for (int col = 0; col < N; col++) {
if (isSafe(row, col)) { // 检查是否可以放置皇后
queenPos[row] = col; // 放置皇后
backtrack(row + 1); // 递归处理下一行
// 回溯:移除当前行的皇后,尝试下一列
}
}
}
int main() {
// 输入皇后数量
printf("请输入皇后数量 N: ");
scanf("%d", &N);
// 动态分配数组
queenPos = (int *)malloc(N * sizeof(int));
if (queenPos == NULL) {
printf("内存分配失败!\n");
return 1;
}
solutionCount = 0; // 初始化解的数量
backtrack(0); // 从第 0 行开始回溯
// 输出结果
printf("共有 %d 个解\n", solutionCount);
// 释放内存
free(queenPos);
return 0;
}
探索提示:尝试运行N=12以上的情况,感受指数级增长的威力!你能优化这个算法吗?