dfs经典皇后问题

[n-皇后问题]

题目描述

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

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

输入格式

共一行,包含整数n。

输出格式

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

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

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

数据范围

1≤n≤9

样例
输入样例:
4
输出样例:
.Q..
...Q
Q...
..Q.

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

首先接受一种最容易理解的方法


#include <iostream> 

using namespace std;

const int N = 20;

char p[N][N];
int n;
bool cnt[N],dg[N],udg[N];//初始时是false

void dfs(int u)
{
    if(u == n)
    {
        for(int i = 0;i < n;i ++) puts(p[i]);
        puts("");//相当于换行
        return;
    }
    for(int i = 0;i < n;i ++)
    {
        if(!cnt[i] && !dg[n - u + i] && !udg[u + i])
        {
            p[u][i] = 'Q';
            cnt[i] = dg[n - u + i] =udg[u + i] = true;
            dfs(u + 1);
            cnt[i] = dg[n - u + i] =udg[u + i] = false;
            p[u][i] = '.';
        }
    }
}
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;
}

大概思路是
1-先把棋盘用二维数组模拟
2-每放一个,把它的每列每行,还有两个对角线给标记,利用递归dfs来遍历每种情况
3-输出


各个字母的解释:
p[N][N]:代表棋盘

n: 皇后个数

cnt[N]:列

dg[N]:对角线(因为这个棋盘我们是看成一个坐标系嘛,其实就是个 \ )

udg[N];反对角线(其实就是个 /)

u:就是初始开始遍历的值一般是为0,也代表行

代码分析

void dfs(int u)
{
    if(u == n)
    {
        for(int i = 0;i < n;i ++) puts(p[i]);
        puts("");//相当于换行
        return;
    }

这部分很简单就是填满了就输出嘛,就是它是直接一行一行输出的,不用两个for循环,更简洁点。

for(int i = 0;i < n;i ++)
    {
        if(!cnt[i] && !dg[n - u + i] && !udg[u + i])
        {
            p[u][i] = 'Q';
            cnt[i] = dg[n - u + i] =udg[u + i] = true;
            dfs(u + 1);
            cnt[i] = dg[n - u + i] =udg[u + i] = false;
            p[u][i] = '.';
        }
    }
}

首先我们来模拟一遍
以样例n=4为例

一开始u等于0进入循环,不满足第一部分(就是不能输出)进入第二部分的for循环,这里i是代表列,进入if语句,一开始cnt[0],dg[4-0+0] ,udg[0+0]都是false,所以必然是满足的,然后分析

为什么dg里面是n-u+i呢

首先n可以是任意值,-u+i是可能小于零的嘛,所以加个n相当于所有的这类值都向上平移到了正值
在这里插入图片描述
因为,dg里面的内容,其实只是满足那一条对角线所有格子的共同点,就比如1那条对角线嘛,所经过的格子是(0.0)(1.1)(2.2)
共同点是截距为0嘛把它看成y=x+b,b=y-x
在这里面是b=i-u嘛,虽然是n-u+i但都一样只是把截距变为了4,向上平移嘛,然后看2那条线经过(0.1)(1.2)可以看到此时b=i-u=0-1
=-1,正因为如此,数组没有负数项,所以要加一个偏移量n其实也能是2n,最小是n-1保证它大于等于0.然后udg和这个类似就是另一个斜向上的斜线,它的截距一定是正数所以不用偏移量。
最后一个解释为什么只有列有标记,行没有,因为我们每一次dfs都会把u进行变化所以相当于把行进行标记了。


先把第一行的第一个棋盘格子放一个Q
把所在列,和两个对角线进行标记,
进入第二层的dfs递归
第一部分不满足,进入for循环部分,然后就是
判断,一开始的0是不满足cnt[i]=false,然后看i=1,又不满足dg[n-u+i]=false,然后再是i=2,发现满足,所以p[1][2]=Q,在把它的列,两个对角线给标记,进入第三层dfs递归
可以发现此时第三行所有的格子都不满足
在这里插入图片描述
然后会退出u=2这层递归,到u=1这层,首先接着dfs(u+1)下面语句,先解除第二个Q的所有标记(列和两个对角线),然后i继续从i=3开始,发现可以又继续以上的步骤,然后u=2的那层发现i=1可以,再继续到u=3,发现又不可以了,然后退到u=2,发现所有点都不满足,一直退到原始的那一层即u=0,把1点所有解除后,i=1可以,p[0][1]=Q,在重复以上这样的步骤,就可以得出答案了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值