akoj-1037-八皇后问题

八皇后问题

Time Limit:1000MS  Memory Limit:65536K
Total Submit:18 Accepted:14

Description

经典的八皇后问题,在一个8*8的棋盘上放置8个皇后,使得不能互相攻击到,皇后的攻击范围的同一行,同一列以及同一个斜线。要求输出所有不会互相攻击到的摆放方式,所有通过旋转,对称都方式得到的摆放方式均认为是不同的摆放方式。棋盘被编号为0-7行,0-7列。


Input

无输入。

Output

每行一个数字代表摆放方式,如01234567代表从第0行放在0列,第1行放在1列,
第2行放在2列,按照升序输出。

Sample Input

Sample Output

04752613
05726314
06357142
06471352
…
只给出部分输出…都给了可以直接拷贝了…

Source

[Submit]   [Go Back]   [Status]   [Discuss]

#include 
#include 
#define max 8
int queen[max], sum=0; /* max为棋盘最大坐标 */

void show() /* 输出所有皇后的坐标 */
{
    int i;
    for(i = 0; i < max; i++)
    {
         printf("%d", queen[i]);
    }
    printf("\n");
    sum++;
}

int PLACE(int n) /* 检查当前列能否放置皇后 */
{
    int i;
    for(i = 0; i < n; i++) /* 检查横排和对角线上是否可以放置皇后 */
    {
        if(queen[i] == queen[n] || abs(queen[i] - queen[n]) == (n - i))
        {
            return 1;
        }
    }
    return 0;
}

void NQUEENS(int n) /* 回溯尝试皇后位置,n为横坐标 */
{
    int i;
    for(i = 0; i < max; i++)
    {
        queen[n] = i; /* 将皇后摆到当前循环到的位置 */
        if(!PLACE(n))
        {
            if(n == max - 1)
            {
                show(); /* 如果全部摆好,则输出所有皇后的坐标 */
            }
            else
            {
                NQUEENS(n + 1); /* 否则继续摆放下一个皇后 */
            }
        }
    }
}

int main()
{
    NQUEENS(0); /* 从横坐标为0开始依次尝试 */
    return 0;
}

八皇后问题各种解法分析


一:回溯法

      这种算法想必学习计算机算法分析与设计的人都应该知道,说白了,这个算法就是一个搜索算法,对一棵树进行深度优先搜索,但是在搜索的过程中具有自动终止返回上一层继续往下搜索的能力,这个算法其实就是一个搜索树,对部分节点进行了剪枝是一种可行的算法,对八皇后这样皇后数较少的问题还能够解决,如果皇后数再大一点就无能为力了,当然我们通过这学习了一种算法。至于具体算法怎么实现在此就不一一详述了,因为大部分网站都有具体的源码,核心思想也基本相同,只是编程应用的数据结构有所不同而已。

 

二:概率算法

     这种算法网络上很少有,在此我只是提供一点思路供爱好者探讨。

     基本思想:首先应用随机函数在一个8*8的矩阵中放入合适的4个皇后(即一半的皇后)然后再应用之前的回溯的方法进行搜索,随后循环这样的一个过程,当时间容许的情况下,很快就可以找到满足所有的解。当然这种方法对回溯法只是进行了一点点的改动,但时间复杂度上将有很大的提高。算法源码在此也不予提供,有兴趣的可以与我联系。

 

三:A*算法

      这种算法原本是人工智能里求解搜索问题的解法,但八皇后问题也同样是一个搜索问题,因此可以应用这种方法来进行求解。这里关于A*算法的定义以及一些概念就不提供了,大家可以上网进行搜索,网上有很多关于A*算法的详细介绍。如果有兴趣的朋友可以借阅一本人工智能的书籍上面有关于A*算法求解八皇后问题的详细解答过程,在此我就不多说了,我只是提供一种学习的思路。

 

四:广度优先搜索

     这个是和回溯法搜索相反的一种方法,大家如果学过数据结构的应该知道,图的搜索算法有两种,一种是深度优先搜索,二种是广度优先搜索。而前面的回溯算法回归到图搜索的本质后,发现其实是深度优先搜索,因此必定会有广度优先搜索解决八皇后问题,由于应用广度优先搜索解决八皇后问题比应用深度优先搜索其空间复杂度要高很多,所以很少有人去使用这种方法,但是这里我们是来学习算法的,这种算法在此不适合,不一定在其他的问题中就不适合了,有兴趣的朋友可以参考任何一本数据结构的数据上面都有关于广度优先搜索的应用。

回溯法求解八皇后问题

问题描述:八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。

 
问题历史:八皇后问题最早是由国际象棋棋手马克斯·贝瑟尔于1848年提出。之后陆续有数学家对其进行研究,其中包括高斯和康托,并且将其推广为更一般的n皇后摆放问题。八皇后问题的第一个解是在1850年由弗朗兹·诺克给出的。诺克也是首先将问题推广到更一般的n皇后摆放问题的人之一。1874年,S.冈德尔提出了一个通过行列式来求解的方法,这个方法后来又被J.W.L.格莱舍加以改进。



转化规则:其实八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。当且仅当 n = 1 或 n ≥ 4 时问题有解。令一个一位数组a[n]保存所得解,其中a[i] 表示把第i个皇后放在第i行的列数(注意i的值都是从0开始计算的),下面就八皇后问题做一个简单的从规则到问题提取过程。
(1)因为所有的皇后都不能放在同一列,因此数组的不能存在相同的两个值。
(2)所有的皇后都不能在对角线上,那么该如何检测两个皇后是否在同一个对角线上?我们将棋盘的方格成一个二维数组,如下:

假设有两个皇后被放置在(i,j)和(k,l)的位置上, 明显,当且仅当|i-k|=|j-l| 时,两个皇后才在同一条对角线上。

算法原型:上面我们搞清楚了在解决八皇后问题之前需要处理的两个规则,并将规则转化到了我们数学模型上的问题,那么这段我们开始着手讨论如何设计八皇后的解决算法问题,最常用的就是回溯法,什么是回溯法?
回溯法(英语:backtracking)是穷尽搜索算法(英语:Brute-force search)中的一种。

回溯法采用试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。回溯法通常用最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种情况:

    * 找到一个可能存在的正确的答案
    * 在尝试了所有可能的分步方法后宣告该问题没有答案

在最坏的情况下,回溯法会导致一次复杂度为指数时间的计算。

明显,回溯的思想是:假设某一行为当前状态,不断检查该行所有的位置是否能放一个皇后,检索的状态有两种:
(1)先从首位开始检查,如果不能放置,接着检查该行第二个位置,依次检查下去,直到在该行找到一个可以放置一个皇后的地方,然后保存当前状态,转到下一行重复上述方法的检索。
(2)如果检查了该行所有的位置均不能放置一个皇后,说明上一行皇后放置的位置无法让所有的皇后找到自己合适的位置,因此就要回溯到上一行,重新检查该皇后位置后面的位置。

是否注意到?如果我们用一个数组来保存当前的状态,上面的检索过程是否有点像堆栈的操作?如果找到可行位置,压栈,如果当前行所有位置不行,将出栈。好了,问题模型逐渐清晰开来了,我们可以定义一个过程,这个过程负责检索的过程,如果检索到当前行某个位置可行,压栈,如果当前行所有位置不行,将执行出栈操作。8皇后问题,我们假定栈的大小为8,如果栈满了,表示找到了可行方法,将执行所有出栈操作。也许有同学会问:如果我找到了一个方法,在进入找下一个可行方法时,该如何做到找出的方法不重复?我们是否需要为每行设定一个状态变量? 其实这个问题的处理方法很简单:其实我们在回溯的时候,每个皇后所在位置就是该行的状态变量,回溯转到下一个位置的时候,只需后移1位即可,也就是i++

OK,其实我们可以使用一个数组来模拟栈的结构就可以了,上面解说的时候不用数组而使用栈是因为栈的结构比数组更形象而已。根据上述想法,我们必须定义一个过程,这个过程用来检查当前行的某个位置是否可行,为了方便大家阅读,我采用了常用的算法描述语言 SPARKS 。SPARKS 有个最大的特点就是非常注重算法的思想而不是代码,这样可以更加清晰明了地帮助读者了解作者的算法思想。

(1)过程PLACE,检索当前行是否可以放置一个皇后。
 procedure PLACE(k)
   //如果一个皇后能放在第K行和X(k)列,则返回true,否则返回false。X是一个全程数组,进入此过程时已设置了k个值。ABS(r)过程返回r的绝对值//
    global X(1:k);integer i,k
    i←1
    while ido
        if X(i)=X(k) or ABS(X(i)-X(k))=ABS(i-k)
            then return (false)
            endif
            i←i+1
        repeat
        return
 (true)
end PLACE
   
(2)利用上述的检索过程,通过递归的方式,来确定每个皇后的位置———回溯的思想
 procedure NQUEENS(n)
    //此过程使用回溯法求出在一个n*n棋盘上放置n个皇后,使其不能互相攻击的所有可能位置//
    integer k,n,X(1:n)
    X(1)←0;k←1    //k是当前行,X(k)是当前行的位置
    while k>0 do
        X(k)←X(k)+1   //移到下一个位置
        while X(k)<=n and not PLACE(k) do    //此处能放这个皇后吗?
            X(k)←X(k)+1
        repeat
        if X(k)<=n               //找到一个位置//
            then if k=n          //是一个完整的解吗?//
                then print(X)      //是,打印数组//
                else k←k+1;X(k)←0     //转向下一行//
                endif
        else k←k-1     //否则,回溯上一行//
        endif
        repeat
end NQUEENS
   
      
C语言八皇后问题的实现:
#include
#include  
#define max 8
int queen[max], sum=0; /* max为棋盘最大坐标 */

void show() /* 输出所有皇后的坐标 */
{
    int i;
    printf("(");
    for(i = 0; i < max; i++)
    {
         printf(" %d", queen[i]);
    }
    printf(")\n");
    sum++;
}
 
int PLACE(int n) /* 检查当前列能否放置皇后 */
{
    int i;
    for(i = 0; i < n; i++) /* 检查横排和对角线上是否可以放置皇后 */
    {
        if(queen[i] == queen[n] || abs(queen[i] - queen[n]) == (n - i))
        {
            return 1;
        }
    }
    return 0;
}
 
void NQUEENS(int n) /* 回溯尝试皇后位置,n为横坐标 */
{
    int i;
    for(i = 0; i < max; i++)
    {       
        queen[n] = i; /* 将皇后摆到当前循环到的位置 */
        if(!PLACE(n))
        {           
            if(n == max - 1)
            {
                show(); /* 如果全部摆好,则输出所有皇后的坐标 */
            }         
            else
            {
                NQUEENS(n + 1); /* 否则继续摆放下一个皇后 */
            }
        }
    }
}
 
int main()
{
    NQUEENS(0); /* 从横坐标为0开始依次尝试 */
    printf("%d", sum);
    system("pause");
    return 0;
}

你可能感兴趣的:(akoj,安科OJ题解)