n皇后问题(DFS)

​DFS解n皇后问题

原题详细如下:
n− 皇后问题是指将 n 个皇后放在 n×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。

现在给定整数 n,请你输出所有的满足条件的棋子摆法。

输入格式

共一行,包含整数 n。

输出格式

每个解决方案占 n 行,每行输出一个长度为 n 的字符串,用来表示完整的棋盘状态。

其中 . 表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。
每个方案输出完成后,输出一个空行。
注意:行末不能有多余空格。
输出方案的顺序任意,只要不重复且没有遗漏即可。

数据范围

1≤n≤9

输入样例:

4

输出样例:

.Q..
...Q
Q...
..Q.
..Q.
Q...
...Q
.Q..

解题思路:

从每一行开始,遍历一这行的所有元素,如果这一行的列,正对角,反对角都没有皇后,就在这个位置放入一个皇后,然后继续向下一行进行搜索。

对角坐标如下图:
n皇后问题(DFS)_第1张图片

代码

#include
using namespace std;
const int N = 20;
int n;
bool y[N], dg[N], udg[N]; //分别对应列,正对角,反对角
char p[N][N];
void dfs(int u)
{
	if (u == n)
	{
		//如果成功搜索到最后一行,就说明已经找到了一个方案,就把这个方案输出
		for (int i = 0; i < n; i++)cout << p[i] << endl;
		cout << endl;
		return;
	}
	for (int i = 0; i < n; i++)
	{
		//遍历这一行当中得元素,如果这一列以及两个对角都没有皇后,就在这个坐标放入一个皇后
		if (!y[i] && !dg[u + i] && !udg[n - u + i]) 
		{
			p[u][i] = 'Q';
			y[i] = dg[u + i] = udg[n - u + i] = true;
			dfs(u + 1); //放完后继续向下一行搜索
			//搜索完之后回溯要把数据还原
			p[u][i] = '.';  
			y[i] = dg[u + i] = udg[n - u + i] = false;
		}
	}
}
int main()
{
	cin >> n;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			p[i][j] = '.';
	dfs(0);
	return 0;
}

蓝色为正对角,绿色为反对角,

故正对角线的坐标为:当前行+当前列,

反对角坐标为:n-当前行+当前列。

思路总结

这段代码使用回溯算法解决了经典的 n-皇后问题。n-皇后问题的目标是在一个 n×n 的棋盘上放置 n 个皇后,使得它们互不攻击(即任意两个皇后不在同一行、同一列或同一对角线上)。下面是对代码的详细解析:


1. 变量定义与初始化

const int N = 20;
int n;
bool y[N], dg[N], udg[N]; // 分别对应列、正对角、反对角
char p[N][N]; // 棋盘
  • n:输入的棋盘大小,表示 n×n 的棋盘和 n 个皇后。
  • y[N]:布尔数组,表示某一列是否已经被占用。y[i] = true 表示第 i 列已经有皇后。
  • dg[N]:布尔数组,表示正对角线是否被占用。dg[u + i] = true 表示正对角线 u + i 已经有皇后。
  • udg[N]:布尔数组,表示反对角线是否被占用。udg[n - u + i] = true 表示反对角线 n - u + i 已经有皇后。
  • p[N][N]:字符数组,表示棋盘。p[i][j] = 'Q' 表示在第 i 行第 j 列放置了一个皇后,p[i][j] = '.' 表示该位置为空。

2. 主函数 main

int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            p[i][j] = '.';
    dfs(0);
    return 0;
}
  • 初始化棋盘:将棋盘的所有位置初始化为 '.',表示初始时棋盘为空。
  • 调用 dfs(0):从第 0 行开始递归搜索所有可能的皇后放置方案。

3. 深度优先搜索函数 dfs

void dfs(int u)
{
    if (u == n)
    {
        // 如果成功搜索到最后一行,就说明已经找到了一个方案,输出这个方案
        for (int i = 0; i < n; i++) cout << p[i] << endl;
        cout << endl;
        return;
    }
    for (int i = 0; i < n; i++)
    {
        // 遍历这一行的所有列
        if (!y[i] && !dg[u + i] && !udg[n - u + i])
        {
            p[u][i] = 'Q'; // 放置皇后
            y[i] = dg[u + i] = udg[n - u + i] = true; // 标记列、正对角、反对角为占用
            dfs(u + 1); // 递归到下一行
            // 回溯:恢复状态
            p[u][i] = '.';
            y[i] = dg[u + i] = udg[n - u + i] = false;
        }
    }
}
  • 参数 u:表示当前正在处理的行。
  • 递归终止条件:当 u == n 时,表示已经成功放置了 n 个皇后,输出当前棋盘的布局。
  • 遍历当前行的每一列
    • 检查当前列 i、正对角线 u + i 和反对角线 n - u + i 是否被占用。
    • 如果没有被占用,则在当前位置 (u, i) 放置一个皇后,并标记列、正对角、反对角为占用。
    • 递归调用 dfs(u + 1),继续处理下一行。
    • 回溯:在递归返回后,撤销当前放置的皇后,恢复列、正对角、反对角的状态,以便尝试其他可能的放置位置。

4. 对角线的计算

  • 正对角线u + i
    在同一正对角线上的所有点满足 行 + 列 = 常数
  • 反对角线n - u + i
    在同一反对角线上的所有点满足 行 - 列 = 常数(这里为了避免负数,使用 n - u + i 作为索引)。

5. 输出格式

  • 每个解决方案输出 n 行,每行是一个长度为 n 的字符串,表示棋盘的当前状态。
  • '.' 表示空位,'Q' 表示皇后。
  • 每个方案输出完成后,输出一个空行。

6. 示例运行

输入:
4
输出:
.Q..
...Q
Q...
..Q.

..Q.
Q...
...Q
.Q..
  • 对于 n = 4,共有 2 种合法的皇后放置方案。

7. 时间复杂度

  • 回溯算法的时间复杂度为 O(n!),因为每一行有 n 种选择,递归深度为 n
  • 对于 n ≤ 9,该算法可以在合理时间内完成。

8. 总结

  • 该代码通过回溯算法系统地搜索所有可能的皇后放置方案。
  • 使用列、正对角、反对角的标记数组来快速判断某个位置是否可以放置皇后。
  • 回溯时恢复状态,确保所有可能的方案都被尝试。

你可能感兴趣的:(深度优先,算法,数据结构)