关于八皇后问题的解法

八皇后问题是十九世纪著名的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

 

八皇后问题不是只用于这个有点无聊的数学问题上,不然就没什么研究意义了。在景观设计,道路布局,城市规划等方面有应用价值。比如布置景观时,要让N个景观互相不遮挡视线。

 

方案一  暴力法

最容易实现的,8层循环嵌套

当然OK,但它的复杂度惊人,O(nn)n为行列数

为了减少循环次数,针对该问题,最外层循环从第一列开始,向内层依次加1,最内层循环就从第八列开始了。

 

 

方案二  回溯法

八皇后问题是回溯法经典的例子

回溯法基本的思想是采用递归,并且分步解决问题

 

首先摆第一行的皇后(都是从第一列开始摆)

然后摆第二行的皇后,摆在第一列,发现不行;摆第二列,也不行;摆第三列,行了。

接下来再摆第三行,都是从第一列开始摆,如果不行就依次往后挪。

以此类推,对于某一行,如果这一行每列都不行,那么就得退回到上一行,(说明上一行的位置就已经不对了),上一行的皇后得往后再挪一格,如果已经是最后一格了,那就再退到上上一行……

如果摆到了最后一行,并且出现了一个解,那么将这个解输出后,同样再退回到上一行,将上一行的皇后往后挪一格……看看还有没有其他解,直到不可能再出现其他解为止。

 

棋盘信息,为了减少每次棋子调整带来的cost,我们可以用四个一维数组来表示。(传统上用一个二维数组表示的话,每次棋子调整要改变数组中很多个元素的值,而且相当混乱)

 

因为每个皇后的控制范围为一行、一列再加对角线,所以与之对应的,我们把棋盘分成8行、8列、15个左下斜对角线和15个右下斜对角线,每一项都用一个一维数组表示。每次摆一个皇后的时候,只需改变将与之对应的行、列、左下斜对角线和右下斜对角线数组中元素(说明此行、列、对角线上不能再放其他皇后了),也就是只需改变4个元素,而且这4个元素的在它们的数组中的下标与该皇后的横纵坐标有关(具体可以自己推算一下)。

 

类声明如下(cpp

class ChessBoard

{

public:

         ChessBoard();

         ChessBoard(int);

         void findSolutions();

private:

         const bool available;  //表示行、列或对角线是否可用

         const int squares, norm;  //squares为行数,normsquares-1(用于推算对角线的下标)

         bool *column, *leftDiagonal, *rightDiagonal;

         int *positionInRow;  //记录每一行皇后的下标(列号)

int howMany;  //已放好的皇后的个数

         void putQueen(int);

         void printBoard(ostream&);

         void initializeBoard();

};

 

关键方法的实现

void ChessBoard::putQueen(int row)

{

         int col;

         //对于每一行,都从第一格开始尝试摆

         for(col=0; col<squares; col++)

         {

if(column[col]==available && leftDiagonal[row+col]==available && rightDiagonal[row-col+norm]==available)  //该格子可用

                   {

                            positionInRow[row]=col;

                            column[col]=!available;

                            leftDiagonal[row+col]=!available;

                            rightDiagonal[row-col+norm]=!available;

                  

                            if(row<squares-1)

                                     putQueen(row+1);  //进入下一行

                            else

                                     printBoard(cout);  //已找到一种解

                           

                            //由于下一行无法满足条件或者已找到一种解而退回来时

//恢复原来的状态,continue

column[col]=available;

                            leftDiagonal[row+col]=available;

                            rightDiagonal[row-col+norm]=available;

                   }

         }

}

 

其实用回溯法的话,也穷尽了每一种情况,也是exhaustive search,而且它的效率还不如暴力法,因为用了递归。

 

 

方案三 Best-First Search

由于我们最初的问题是求所有的摆法,所以不管怎样它一定是一个exhaustive search问题了。如果我们把问题改一下,对任意一种棋盘状态(八行里每行都只有一个皇后),怎样才能最快的调整为一个满足条件的状态。那这样的话,就不能再像之前那样盲目的去搜索了,得采用一定的策略了,就是Informed SearchBest-First SearchA*Hill ClimbingTabu Search等都能很好的解决该问题,我们以最基础的Best-First Search来讲述。

 

为了简化问题,我们先来看一个4X4的棋盘。

 

 

 

给定一个初始状态,我们每次都对它挪动一个棋子(也就是扩展后继节点)。第一张图中,第一行的棋子能朝左或右挪一格,第二行只能朝右挪一格,第三行第四行都能朝左或右挪一格。图中h表示深度,也就是调整的次数;g表示当前的Conflicts数,就是不符合的行、列和对角线总数。(图中可能hg画反了,真正的h应该代表当前节点离目标节点的cost,也就是图中的g;真正的g应该代表初始节点到当前节点的cost,也就是图中的h

根据Best-First Search算法,每次都将当前最好节点的所有后继节点都插入OPEN List中,每次都选择OPEN list中最好的节点进行扩展,直到选出的节点即为目标节点(图中的g=0)。每次都沿着当前最好的节点延伸,很快就能找到目标节点。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
摘要: 八皇后问题要求在一个8*8的棋盘上放上8个皇后,使得每一个皇后既攻击不到另外七个皇后,也不被另外七个皇后所攻击.按照国际象棋规则,一个皇后可以攻击与之处在同一行或同一列或同一斜线上的其他任何棋子.因此,八皇后问题等于要求八个皇后的任意两个不能放在同一行或同一列或同一斜线上。 而本课程设计本人的目的也是通过用c++语言平台将一个8*8的棋盘上放上8个皇后,使得每一个皇后既攻击不到另外七个皇后,也不被另外七个皇后所攻击的92种结构予以实现. 使用递归方法最终将其问题变得一目了然,更加易懂。 关键词: 八皇后 ; c++ ; 递归法 目 录 1. 课题综述 1 1. 1课题的来源及意义 1 1. 2 面对的问题 1 2. 需求分析 1 2. 1 涉及到的知识 1 2. 2 软硬件的需求 1 2. 3 功能需求 2 3. 概要设计 2 4. 详细设计和实现 2 4. 1 算法描述及详细流程图 2 4.1.1 算法描述 3 4.1.2 算法流程图 3 5. 代码编写及详细注释 4 6. 程序调试 7 6. 1调试过程、步骤及遇到的问题 7 7. 运行与测试 7 7.1运行演示 7 总 结 9 致 谢 10 参考文献 11 . 1. 课题综述 1. 1课题的来源及意义 八皇后问题是一个古老而著名的问题,该问题是十九世纪著名的数学家高斯1850年提出的。 在国际象棋皇后是最有权利的一个棋子;只要别的棋子在它的同一行或同一列或同一斜线(正斜线或反斜线)上时,它就能把对方棋子吃掉。所以高斯提出了一个问题:在8*8的格的国际象棋上摆放八个皇后,使其不能相互攻击,即任意两个皇后不能处于同一列、同一行、或同一条斜线上面,问共有多少种解法。 到了现代,随着计算机技术的飞速发展,这一古老而有趣的数学游戏问题也自然而然的被搬到了计算机上。运用所学计算机知识来试着解决这个问题是个锻炼和提高我自己编程能力和独立解决问题能力的好机会,可以使我增强信心,为我以后的编程开个好头,故我选择了这个有趣的课题。 1. 2 面对的问题 1) 解决冲突问题: 这个问题包括了行,列,两条对角线; 列:规定每一列放一个皇后,不会造成列上的冲突; 行:当第I行被某个皇后占领后,则同一行上的所有空格都不能再放皇后,要把以I为下标的标记置为被占领状态; 2) 使用数据结构的知识,用递归法解决问题。 2. 需求分析 2. 1 涉及到的知识 本次课程设计,用到的主要知识有:递归法的运用,for语句的灵活运用,数据结构树知识的灵活运用、栈及数组的掌握。 2. 2 软硬件的需求 1)系统要求:win98以上操作系统; 2) 语言平台:tc++或vc++6.0; 2. 3 功能需求 当运行程序时,在屏幕上显示每一种方法八个皇后的相对位置,要用比较直观 的界面显示。 3. 概要设计 本课件学生是用循环递归循环来实现的,分别一一测试了每一种摆法,并把它拥有的92种变化表现出来。在这个程序,我的主要思路以及思想是这样的: 1)解决冲突问题: 这个问题包括了行,列,两条对角线; 列:规定每一列放一个皇后,不会造成列上的冲突; 行:当第I行被某个皇后占领后,则同一行上的所有空格都不能再放皇后,要把以I为下标的标记置为被占领状态; 对角线对角线有两个方向。在这我把这两条对角线称为:主对角线和从对角线。在同一对角线上的所有点(设下标为(i,j)),要么(i+j)是常数,要么(i-j)是常数。因此,当第I个皇后占领了第J列后,要同时把以(i+j)、(i-j)为下标的标记置为被占领状态。 2)数据结构的实现 而对于数据结构的实现,学生则是着重于: 数组a[I]:a [I]表示第I个皇后放置的列;I的范围:1..8; 对角线数组:b[j](主对角线),c[j](从对角线),根据程序的运行,去决定主从对角线是否放入皇后; 4. 详细设计和实现 4. 1 算法描述及详细流程图 4.1.1 算法描述 A、 数据初始化。 B、 从n列开始摆放第n个皇后(因为这样便可以符合每一竖列一个皇后的要求),先测试当前位置(n,m)是否等于0(未被占领)。如果是,摆放第n个皇后,并宣布占领(记得姚横列竖列斜列一起设置),接着进行递归;如果不是,测试下一个位置(n,m+1),但是如果当n<=8,m=8时,发现此时已无法摆放时,便要进行回溯。从问题的某一种可能出发,搜索从这种情况能出发,继续搜索,这种不断“回溯”的寻找解的方法,称为“回溯法”。 C、使用数组实现回溯法的思想。 D、当n>8时,便打印出结果。 E、输出函数我使用printf输出,运行形式为:第m种方法为:* * * * * * * *

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值