题目描述
- n−皇后问题是指将
n
个皇后放在n×n
的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。 - 现在给定整数
n
,请你输出所有的满足条件的棋子摆法。
输入格式
- 共一行,包含整数
n
。
输出格式
- 每个解决方案占
n
行,每行输出一个长度为n
的字符串,用来表示完整的棋盘状态。 - 其中
.
表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。 - 每个方案输出完成后,输出一个空行。
- 注意:行末不能有多余空格。
- 输出方案的顺序任意,只要不重复且没有遗漏即可。
数据范围
1 ≤ n ≤ 9
基本思路
- 本题的基本思路是使用深度优先搜索算法来查找所有可能的排列情况。在我个人看来,深度优先搜索算法实质上是一种带回溯的蛮力列举算法。
- 对于n皇后问题,我们可以一行一行地来摆放皇后,每一次摆放皇后,都检查新摆放的皇后是否与已经摆放的皇后的位置发生冲突,即位于同一列、同一正对角线和同一反斜线。如果发生了冲突,则当前位置不能考虑,需要考虑下一个位置。
- 每次完成一次皇后摆放后,都可以递归地继续摆放下一行的皇后,直到每一行都摆放了一个不与棋盘上其他皇后冲突的皇后,此时输出摆放结果。
实现代码
#include <cstdio>
int n;
// 使用二维布尔数组定义一个棋盘
const int N = 10;
bool checkerboard[N][N] = {false};
// 用于输出棋盘状态的函数
void print_checkerboard(int n)
{
for(int row = 0; row < n; ++ row)
{
for(int col = 0; col < n; ++ col)
{
if(checkerboard[row][col] != true) printf(".");
else printf("Q");
}
printf("\n");
}
printf("\n");
}
// 用于判断当前位置摆放的皇后是否存在列冲突的函数
bool in_the_same_col(int row, int col)
{
bool flag = false;
for(int i = 0; i < row; ++ i)
{
if(checkerboard[i][col] == true)
{
flag = true;
break;
}
}
return flag;
}
// 用于判断当前位置摆放的皇后是否存在正对角线冲突的函数
bool in_the_same_forward_slash(int row, int col)
{
bool flag = false;
for(int i = 0; i < row; ++ i)
{
if(col - row + i < n && col - row + i >=0 && checkerboard[i][col - row + i] == true)
{
flag = true;
break;
}
}
return flag;
}
// 用于判断当前位置摆放的皇后是否存在反对角线冲突的函数
bool in_the_same_backward_slash(int row, int col)
{
bool flag = false;
for(int i = 0; i < row; ++ i)
{
if(col + row - i < n && col + row - i >= 0 && checkerboard[i][col + row - i] == true)
{
flag = true;
break;
}
}
return flag;
}
// 用于输出n皇后问题所有可能情况的函数
void n_queens(int current_row)
{
// 如果当前所处的行已经越界,则说明找到了一种可行的情况
if(current_row >= n) print_checkerboard(n);
// 如果所处的行还没有发生越界,则需要继续进行皇后摆放
else
{
// 寻找一个不发生矛盾的列,即不与已有的皇后同列,不与已有的皇后同斜线
for(int col = 0; col < n; ++ col)
{
// 如果与已有的皇后发生任意一种冲突,则寻找下一个位置
if(in_the_same_col(current_row, col) ||
in_the_same_forward_slash(current_row, col) ||
in_the_same_backward_slash(current_row, col)) continue;
// 在当前行找到了不发生任何冲突的位置,则记录摆放位置,并准备摆放下一行
else
{
// 记录当前摆放的位置
checkerboard[current_row][col] = true;
// 摆放下一行
n_queens(current_row + 1);
// 执行完成后撤销当前的摆放位置并回到上一行(相当于回溯)
checkerboard[current_row][col] = false;
}
}
}
}
int main(void)
{
scanf("%d", &n);
n_queens(0);
return 0;
}
注意事项:
- 对于深度优先搜索(DFS)算法,并没有固定的算法模板,但是这一类算法都遵循相同的逻辑。这个逻辑就是把一个大的问题拆分为多个相似的步骤,每个步骤时只需要考虑当前可以选择的道路,然后以当前的分岔口作为新的起点,递归地进行搜索即可。
- 本题中,首先在主函数部分输入棋盘的长度,然后调用自定义的
n_queens
函数,表示摆放第0行的皇后;n_queens
皇后中的处理情况分为两类:如果传入的参数已经超过了棋盘的大小,说明棋盘上各行都已经摆放好了一个不与其他的皇后冲突的皇后,此时可以直接进行输出。如果传入的参数没有超过棋盘大小,而是棋盘中某一行的行号,说明此时需要摆放该行的皇后,我们只需要通过遍历该行的每一列,找到可以摆放皇后的位置即可。这些位置需要满足不发生列冲突和斜线冲突。列冲突通过自定义的in_the_same_col
函数进行判定,斜线冲突分为正斜线冲突和反斜线冲突,分别通过自定义的in_the_same_forward_slash
函数和in_the_same_backward_slash
函数进行判定。 - 三个用于判定冲突的函数需要注意检查下标是否越界。
- 在摆放当前行的皇后的函数中,递归地调用
n_queens
函数摆放下一行的皇后,但是需要注意执行完成后需要进行状态恢复,即将本行摆放的皇后撤走。