AcWing 843. n-皇后问题

原题链接如下:
AcWing 843. n-皇后问题
来自y总算法基础课第三讲搜索与图论,而n-皇后问题众所周知是dfs的典型问题
原题详细如下:
n− 皇后问题是指将 n 个皇后放在 n×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
AcWing 843. n-皇后问题_第1张图片
现在给定整数 n,请你输出所有的满足条件的棋子摆法。

输入格式
共一行,包含整数 n。

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

其中 . 表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。

每个方案输出完成后,输出一个空行。

注意:行末不能有多余空格。

输出方案的顺序任意,只要不重复且没有遗漏即可。

数据范围
1≤n≤9
输入样例:
4
输出样例:

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

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

参考代码:

#include
using namespace std;
const int N = 20;

int n;
char g[N][N];//存放题目所需的棋盘 每个槽初始值默认为.
bool col[N],dg[N],udg[N];//记录当前列,当前正对角线,当前反对角线使用过否

void dfs(int u)
{
    //到达递归的结束的边界 输出放置好皇后的地图
    if(u==n)
    {
        for(int i = 0;i<n;i++) puts(g[i]);
        puts("");
        return;//不要忘记return 不然会无休止的运行 更不会回溯
    }
    //尝试每一列的可能
    for(int i = 0;i<n;i++)
    {
        //判断横行u和竖列i构成的坐标(u,i) 在这列 在这正对角线和反对角线是否放置了皇后
        if(!col[i]&& !udg[u+i]&& !dg[u-i+n])
        {
            g[u][i] = 'Q';
            col[i] = udg[u+i] = dg[u-i+n] = true;//标记这个放过皇后的点
            dfs(u+1);//继续到下一行中的位置
            col[i] = udg[u+i] = dg[u-i+n] = false;//恢复现场
            g[u][i] = '.';
        }
    }
}
int main()
{
    cin>>n;
    for(int i = 0;i<n;i++)
    {
        for(int j = 0;j<n;j++)
        {
            g[i][j] = '.';
        }
    }
    dfs(0);
    return 0;
}

问题解析:
n-皇后问题的核心就是将每个位置进行试探,保证每个皇后不在同一列,不在同一对角线,也不在同一反对角线。这个试探的过程其实就是深度优先搜索bfs的过程,我们通过bfs把每一行可能在哪个地方放置皇后的方案都进行了搜索并且输出,所以我们能达到题目中要求的输出每一种可能的解决方案。

重点细节解析:
核心函数:void dfs(int u)

边界情况:

    //到达递归的结束的边界 输出放置好皇后的地图
    if(u==n)
    {
        for(int i = 0;i<n;i++) puts(g[i]);
        puts("");
        return;//不要忘记return 不然会无休止的运行 更不会回溯
    }

对当前行中的列的每一个可能值的尝试

 for(int i = 0;i<n;i++)
 {
 ...
 }

剪枝过程:

if(!col[i]&& !dg[u+i]&& !udg[n-u+i])

剪枝就是为了减少搜索树的规模,因为当发现某个位置不能防止皇后,我们就跳过寻找下一个列位置,不然用这个错误的位置还往下走搜寻是多余的,再继续下去也是错误的。
剪枝的实现思路:我们采用的是一元一次方程,我们利用正反对角线在函数上的特性,整个棋盘我们看成一个平面直角坐标系,u代表横行的话就相当于y坐标,i代表列的话就相当于x坐标,则正对角线方程为u = i+b,即b = u-i,同时应该注意的是u-i可能是负数,所以我们需要加个偏移量n,保证参数为正。的而反对角线方程为 u = b - i,即b = u+i
虽然u和i的值在不断变化,但是总和是不变的,因此会形成两条正反对角线。对于相减可能为负的情况,添加偏移量n。所以就可以用这种方式来搜素各个位置的状态。

    for(int i = 0;i<n;i++)
    {
        //判断横行u和竖列i构成的坐标(u,i) 在这列 在这正对角线和反对角线是否放置了皇后
        if(!col[i]&& !udg[u+i]&& !dg[u-i+n])
        {
            g[u][i] = 'Q';
            col[i] = udg[u+i] = dg[u-i+n] = true;//标记这个放过皇后的点
            dfs(u+1);//继续到下一行中的位置
            col[i] = udg[u+i] = dg[u-i+n] = false;//恢复现场
            g[u][i] = '.';
        }
    }

至此,基本上这个问题关键问题都给解决了,本人讲解能力有限,感兴趣的同学们可以去AcWing官网看看大佬们的题解,就在原题链接的题解部分

你可能感兴趣的:(Acwing,算法,c++,dfs)