关闭

八皇后问题与深度优先搜索

1096人阅读 评论(0) 收藏 举报
分类:

     问题大家应该已经比较的清楚了,在一个n*n的棋盘里面如何放置n个皇后使得皇后不相互攻击。 

     在讲我的代码之前我先给大家稍微讲一下深度优先搜索。 

      首先我们在数据结构课的时候就已经知道什么是图的深度优先遍历,那么我们其实所有的问题都可以这么抽象成一个图:当前状态表示一个点,通过某种动作可以直接相连的状态相互之间连接一条边。以迷宫问题举一个例子:比如我现在在(4,3)这个点,那么我现在可以选择向上向下向左向右四个方向走过去,为了不重复计算我们将走过的点都不连接,所以说我们可以将这个点和它上下左右没有被遍历过的四个点连接起来,以此类推,我们可以得到一个树,这个树可以理解为就是我们的状态转移的树。我们这个题目的任务就是去找到一条从根到目标节点的满足条件的路径。树上找一条道到规定点的路径有两种找法,一种是先选定一种策略不停的找下去,如果找不到那么就换一个策略来搜,直到找到或者确认不存在这个点为止。

     对于这个n皇后问题,我们可以这么思考:

      1.从第一行开始找,先找到一个可以放置棋子的点,然后标记。

      2.找下一行,先检查是否和前面的棋子在一条线上或者在主对角线和斜对角线上,如果都不在,那么记录下这个点已经放置了一枚棋子,然后再搜下一行。

     其实我们可以发现第一步和第二步性质上是一样的,只是规模小了一点,所以说我们可以用递归来实现这个思路。

    1.附上一个深度优先搜索的伪代码和搜索套路:

      

void dfs(){
    if (到达目标){
        相应动作;
       return;
   }
   if (边界条件){
      return ;
  }
  //状态转移的code

}

这个样子不断地往下扩充的话我们就可以把整迷宫问题的可能的解隐藏在这个树里面了,树的第i层的所有的点表示的就是经过i步以后我们可以到达的所有的位置,因此为了找到一个最近的解只需要在这个树里面寻一条路径可以走到终点就可以了。

我们用n皇后问题来看这个东西怎么弄。

n皇后问题说到底就是一个搜索的问题,一到搜索,我们就考虑像迷宫问题类似的,这棵树怎么建?

应该很好想的,树的第i层表示棋盘的第i层,那么每个节点往下就扩展八个节点,这个父节点的第i个节点表示在下一行的第i个位置放置一个皇后。如图所示:(只画一个节点怎么扩展因为这个树实在是太大了- -

但是 我们可以脑补一下- - 最后这个树画出来实在是太大了- - 遍历也很要花时间。怎么办?有些地方 可以 不!用!搜!

1:首先我们不需要搜和上一个同列的地方

2:我们不用搜和上一个在对角线的地方。

这个搜索的技巧我们叫做 剪枝。

优先剪枝比后剪枝好,因为越先剪枝可以减掉的状态越多,搜的越快。

        像上图,假设3在树的第一层,表示第一行第三列放置了一个皇后,后面的就是第二行放置皇后的情况,我们就可以看见,首先3不用搜,因为与皇后同列,2和4也不用搜,因为和(1,3)在一个对角线上,如果各位不嫌麻烦的话可以手动模拟一下,这样子会极大地加速皇后判定出解的速度。

后面我分享一下我一般做dfs的想法:

到达目标:搜索到第n层(从第0层记起),因为只有碰到了可以放置的棋子的地方才可以向下搜,所以说如果搜到了第n行表示前n-1行已经放置妥当。

边界:无,因为我有一个check函数来测试当前点是否可行,如果不可行就不递归。所以说不需要检查边界。

状态转移:尝试这一行每一个点,如果这个点可以放置棋子那么这一列的vis标记打上1,表示已经占用,再递归dfs(n+1)行,递归完了以后记得标记要清掉,因为这个棋子要放到下一个地方去了:)

如何检查对角线:

前面我们用一个一维数组记录了第n行第几个位子放置棋子,这个记录棋盘状态的一位数组我们记成chess[100],那么如果存在一个j 使得当前在第n行第i列的一个棋子满足

j-chess[j]==n-i 或者j+chess[j]==n+i那么我们就可以判断这个棋子和前面的第j行的棋子冲突了。大家这里可以手动枚举一下。

check函数在第0行时特判全部是可以的(显而易见从第0行开始放的话怎么放都是可以的。)

看具体代码:

#include <bits/stdc++.h>
using namespace std;
int chess[20],vis[20];
int nl;
int cnt;
void print(){
   for (int i=0;i<nl;i++){
      for (int j=0;j<nl;j++){
          if (j==chess[i]) printf("Q");
          else printf("*");
      }
      puts("");
   }
}
bool check (int n,int i){
    if (n==0) return true;
    for (int j=0;j<n;j++){
        if (j-chess[j]==n-i) return false;
        if (j+chess[j]==n+i) return false;
    }
    return true;
}
void dfs(int n)
{
   if (n==nl) {
        cnt++;
        //cout<<"case "<<cnt<<":"<<endl;
        //print();
        return ;
   }
   for (int i=0;i<nl;i++){
       if (check(n,i)&&!vis[i]) {
          vis[i]=1;
          chess[n]=i;
          dfs(n+1);
          vis[i]=0;
       }
   }
}
int main()
{
    while (scanf("%d",&nl)==1)
    {
        cnt=0;
        memset(vis,0,sizeof(vis));
        memset(chess,0,sizeof(chess));
        dfs(0);
        cout<<cnt<<endl;
    }
    return 0;
}


1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:5933次
    • 积分:264
    • 等级:
    • 排名:千里之外
    • 原创:21篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条