八皇后课程设计报告.docx
《八皇后课程设计报告.docx》由会员分享,可在线阅读,更多相关《八皇后课程设计报告.docx(25页珍藏版)》请在冰豆网上搜索。
八皇后课程设计报告
淮阴工学院
C++程序设计课程设计报告
选题名称:
八皇后
系(院):
计算机工程系
专业:
计算机科学与技术
班级:
计算机1084
姓名:
XXX学号:
XXXXXXXXXX
指导教师:
戴峻峰、赵建洋
学年学期:
2008~2009学年第2学期
2009年6月13日
设计任务书
课题
名称
八皇后
设计
目的
1.调研并熟悉八皇后的基本功能、数据流程与工作规程;
2.学习八皇后相关的算法和基于VC++集成环境的编程技术;
3.通过实际编程加深对基础知识的理解,提高实践能力;
4.学习开发资料的收集与整理,学会撰写课程设计报告。
实验
环境
1.微型电子计算机(PC);
2.安装Windows2000以上操作系统,VisualC++6.0开发工具。
任务
要求
1.利用课余时间去图书馆或上网查阅课题相关资料,深入理解课题含义及设计要求,注意材料收集与整理;
2.在第16周末之前完成预设计,并请指导教师审查,通过后方可进行下一步工作;
3.本课题要求至少用三种方法解决八皇后问题,输入棋盘的阶层,然后显示共有多少种布局方案,并显示每一种方案的具体情况。
4.结束后,及时提交设计报告(含纸质稿、电子稿),要求格式规范、内容完整、结论正确,正文字数不少于3000字(不含代码)。
工作进度计划
序号
起止日期
工作内容
1
2009.05.25~2009.05.30
在预设计的基础上,进一步查阅资料,完善设计方案,形成书面材料。
2
2009.06.1~2009.06.2
设计总体方案,构建、绘制流程框图,编写代码,上机调试。
3
2009.06.3~2009.06.4
测试程序,优化代码,增强功能,撰写设计报告。
4
2009.06.5
提交软件代码、设计报告,参加答辩,根据教师反馈意见,修改、完善设计报告。
指导教师(签章):
年月日
摘要:
八皇后问题。
这是由大数学家高斯于1850年首先提出来的,要求在8*8的方格国际象棋盘上放置8个皇后,任意两个皇后不能位于同一行,同一列或同一斜线(正斜或反斜线)上。
输出所有可能的放法。
分析:
解此问题的基本思路为:
开始时棋盘为空,第二个皇后可在第一行的任意位置上放置。
一旦放置好第一个皇后,以后各行放置皇后时需检查当前列和同一斜线上是否已放置了皇后。
如果未放置,则当前位置可放新的皇后,否则需试新的位置。
假设前行的皇后已放好,现从当前行的第1列起选择适当的位置放置第5个皇后。
显然第1,2列不能放置,因为这两个位置与第1,2行皇后的位置起冲突。
第1个合适的位置是第3列(第4列也可以)。
如果试探结果本行中没有合适的位置,则说明前面的皇后放置的位置不对,应回退到上一行,释放皇后原来占据的位置,改试下一个合适的位置。
如果回退一行后本行仍然没有合适的位置,则继续回退;一旦当前的皇后放置好后,则前进一行放置下一个皇后。
如此下去,直至8个皇后化全部放置好后,则前进一行放置下一个皇后。
如此下去,直至8个皇后全部放置好时即可打印一个解。
关键词:
八皇后,回溯法(试探法),递归法
1课程综述
在国际象棋中,皇后是一个威力很大的棋子。
她可以“横冲直撞”(在水平或垂直方向走任意步数),也可以“斜刺冲杀”(在正负对角线上走任意步数),所以在8*8的棋盘上要布互相不受攻击的皇后,最多只能布8个,共有92种布法,再也不能有别的布法了。
这就是著名的八皇后问题。
1.1课程来源及意义
八皇后问题是一个古老而著名的问题,是回溯算法的典型例题。
该问题是十九世纪著名的数学家高斯1850年提出:
在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
高斯认为有76种方案。
1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。
这就是著名的八皇后问题。
八个皇后在排列时不能同在一行、一列或一条斜线上。
在8!
=40320种排列中共有92种解决方案。
1.2预期目标
运用计算机来完成这个问题,使得没有一个皇后能“吃掉”任何其他一个皇后,即没有任何两个皇后被放置在棋盘的同一行、同一列或同一斜线上。
要求编一个程序求出该问题的所有解,并打印输出结果。
1.3面对的问题
如何选择合适的编程方法来解决问题,以及如何运用没有学过的知识来完成课程设计的要求。
如何使方法程序和主界面程序连接在一起。
1.4需解决的关键技术
要学会回溯法来解决问题,自己对回溯法掌握的还不是很好。
对于递归法缺乏详细的理解,要在练习中慢慢熟悉。
2需求分析联系
2.1涉及的知识基础
2.1.1国际象棋简介
国际象棋是科学、文化、艺术、竞技融为一体的智力体育项目。
它有助于开发智力,培养逻辑思维和想象能力,加强分析能力和记忆力,提高思维的敏捷性和严密性。
它能培养人们战术技术思想意识和全局观点,坚强人们工作中的计划性和灵活性。
它还能丰富人们的文化生活,增进友谊,陶冶高尚情操,培养顽强勇敢、坚毅沉着、机智灵活等优秀的意志品质。
国际象棋着法多变,趣味横溢,对于开发少年儿童的智力,尤有特别好的效果。
因此,目前世界上已有不少国家把国际象棋列入小学课程。
现制国际象棋在我国开展的时间不算长,从1956年开始,国际象棋才和我国传统的中国象棋与围棋一起列入国家开展的体育项目。
从此,这项新兴运动项目发展很快,优秀选手不断涌现。
在多次国际比赛获得优秀成绩的激励、鼓舞和吸引下,特别是谢军两次获得女子世界冠军的鼓舞推动下,我国青少年和儿童学下国际象棋的越来越多了。
2.1.2方法简介
1、回溯法:
使用回溯算法求解的问题一般有这样的特征,要求解问题必须分为若干步,每一步都有几种可能的选择,而且往往在某个选择不成功时需要回头再试另外一种选择,如果到达求解目标则每一步的选择构成了问题的解,如果回头到第一步且没有新的选择则问题求解失败。
通用的回溯算法可用PDL语言描述如下:
初始化,做试探的准备 do { 选择当前步的可能求解路线 if (有可能的解路线) 前进一步 else { 清除当前选择所遗留的痕迹 回溯一步 } } while(没有到最后一步且没有回到初始状态) if (到最后一步) 每一步的选择就是问题的解 else 问题没有解。
回溯算法有两个关键的地方,一是要记住每一步的选择以便不重复选择以及最后能够输出整个解,二是在回溯到前一步的时候要消除当前步的影响。
根据回溯算法,显然要一个数据结构来记住每一步的选择,我们可通过使用一个数组
solution记住每一行皇后放置的列位置来反映每一步的选择。
为更好地选择下一
步放置皇后的列位置,可使用一个二维数组board来模拟棋盘,并记住什么位置放了皇后。
在确定数据结构之后,我们对上述回溯算法作进一步的细化,需要确定几个问题:
怎样的状态是初始状态, 怎样选择当前步可能的路线 怎样表示向
前推进一步, 怎样回溯及清除当前步的痕迹 ?
显然初始状态应该是solution数
组中所有的值为一个特定的不可能的选择,例如为-1或n,其中n是棋盘的宽度,可能的选择是0到n-1。
还有棋盘board的所有值应该表示没有放皇后。
选择当前步可能的求解路线应该是判断当前行可在哪个列位置放皇后。
也就是说求解八皇后问题的每一步就是在每一行选择一个列位置放皇后。
这里的关键是要考虑到当前行的状态有两种:
当前行在这一次的求解中还没试探过,这时考虑放皇后的列位置应该从0开始;当前行在这一次的求解中已经放过皇后,这时再处在当前行是上一步回溯而来,那么这时再试探当前行放皇后的可能列位置应该从上一次在当前行试过的列位置的下一列开始考虑。
向前推进一步应该是记住当前行放皇后的列位置,并且在棋盘上做标记,然后将当前行加1这时要记住如果当前行曾经放过皇后,那么要先消除上一次放皇后的标记。
回溯一步应该是置当前行没有放过皇后,然后将当前行减1这时要记住如果当前行曾经放过皇后,那么要先消除上一次放皇后的标记。
经过这种细化之后,应该很容易写出求出八皇后问题一个解的算法,如果要求出所有的解则可以通过在最后一步再回溯而得到。
再回溯的意思是说,在最后一步再从上一个解的列位置的下一位置再考虑放皇后,如果没有下一列位置,那么回溯到上一行。
2、递归法:
所谓“递归”就是允许程序调用自己本身的过程或函数。
然而此程序却不能无限制地调用自己,否则将永不停止。
也就是说,它必须有一个“终止条件”,当程序碰到此终止条件时就不再调用自己,而直接将结果算出并返回给调用者。
2.2解决问题的思路
解此问题的基本思路为:
开始时棋盘为空,第二个皇后可在第一行的任意位置上放置。
一旦放置好第一个皇后,以后各行放置皇后时需检查当前列和同一
斜线上(图1中所示的AB和CD)是否已放置了皇后。
如果未放置,则当前位置可放新的皇后,否则需试新的位置。
例如图1中所示,假设前行的皇后已放好,现从当前行的第1列起选择适当的位置放置第5个皇后。
显然第1,2列不能放置,因为这两个位置与第1,2行皇后的位置起冲突。
第1个合适的位置是第3
列(第4列也可以)。
如果试探结果本行中没有合适的位置,则说明前面的皇后
放置的位置不对,应回退到上一行,释放皇后原来占据的位置,改试下一个合适的位置。
如果回退一行后本行仍然没有合适的位置,则继续回退;一旦当前的皇后放置好后,则前进一行放置下一个皇后。
如此下去,直至8个皇后化全部放置好后,则前进一行放置下一个皇后。
如此下去,直至8个皇后全部放置好时即可打印一个解。
打印一个解后,可开始寻找新的解。
其方法是认为第8个皇后放置不成功,如前所述:
退回到上一行,释放皇后原来占据的位置,改试下一个合适的位置。
当8个皇后重新全部放置好的时就得到一个新解。
如果一直回退到第一行都再也找不到新解时退职得到了全部解。
分析可知,每次放置皇后时,一般要经过多次尝试,所以可首先考虑递归函数解法。
实现方法如下:
求解过程从空配置开始。
在第1列至第m列为合理配置的基础上,再配置第m+1列,直至第n列配置也是合理时,就找到了一个解。
接着改变第n列配置,希望获得下一个解。
另外,在任一列上,可能有n种配置。
开始时配置在第1行,以后改变时,顺次选择第2行、第3行、…、直到第n行。
当第n行配置也找不到一个合理的配置时,就要回溯,去改变前一列的配置。
引入一个一维数组(col[]),值col[i]表示在棋盘第i列、col[i]行有一个皇后。
例如:
col[3]=4,就表示在棋盘的第3列、第4行上有一个皇后。
另外,为了使程序在找完了全部解后回溯到最初位置,设定col[0]的初值为0当回溯到第0列时,说明程序已求得全部解,结束程序运行。
为使程序在检查皇后配置的合理性方面简易方便,引入以下三个工作数组:
(1)数组a[],a[k]表示第k行上还没有皇后;
(2)数组b[],b[k]表示第k列右高左低斜线上没有皇后;
(3)数组c[],c[k]表示第k列左高右低斜线上没有皇后;
棋盘中同一右高左低斜线上的方格,他们的行号与列号之和相同;同一左高右低斜线上的方格,他们的行号与列号之差均相同。
数据结构与算法:
不需要特殊的数据结构;
使用数组实现回溯法的思想;
初始时,所有行和斜线上均没有皇后,从第1列的第1行配置第一个皇后开
始,在第m列col[m]行放置了一个合理的皇后后,准备考察第m+1列时,在数组a[]、b[]和c[]中为第m列,col[m]行的位置设定有皇后标志;当从第m列回溯到第m-1列,并准备调整第m-1列的皇后配置时,清除在数组a[]、b[]和c[]中设置的关于第m-1列,col[m-1]行有皇后的标志。
一个皇后在m列,col[m]行方格内配置是合理的,由数组a[]、b[]和c[]对应位置的值都为1来确定。
2.3总体方案
求解八皇后问题的每一步就是在每一行选择一个列位置放皇后。
这里的关键是要考虑到当前行的状态有两种:
当前行在这一次的求解中还没试探过,这时考虑放皇后的列位置应该从0开始;当前行在这一次的求解中已经放过皇后,这时再处在当前行是上一步回溯而来,那么这时再试探当前行放皇后的可能列位置应该从上一次在当前行试过的列位置的下一列开始考虑。
向前推进一步应该是记住当前行放皇后的列位置,并且在棋盘上做标记,然后将当前行加1。
这时要记住如果当前行曾经放过皇后,那么要先消除上一次放皇后的标记。
回溯一步应该是置当前行没有放过皇后,然后将当前行减1.这时要记住如果当前行曾经放过皇后,那么要先消除上一次放皇后的标记。
经过这种细化之后,应该很容易写出求出八皇后问题一个解的算法,如果要求出所有的解则可以通过在最后一步再回溯而得到。
再回溯的意思是说,在最后一步再从上一个解的列位置的下一位置再考虑放皇后,如果没有下一列位置,那么回溯到上一行。
2.4功能模块框图
3模块及算法设计
3.1算法描述
数据初始化。
从n列开始摆放第n个皇后(因为这样便可以符合每一竖列一个皇后的要求),先测试当前位置(n,m)是否等于0(未被占领):
如果是,摆放第n个皇后,并宣布占领(要横列竖列斜列一起来),接着进行递归;如果不是,下一个位置(n,m+1),但是如果当n<=8,m=8时,却发现此时已经无法摆放时,便要进行回溯。
当n>8时,便一一打印出结果。
3.2实现方法
1)算法开始,清空棋盘,当前行设为第一行,当前列设为第一列
2)在当前行,当前列的位置上判断是否满足条件(即保证经过这一点的行,列与斜线上都没有两个皇后),若不满足,跳到第4步
3)在当前位置上满足条件的情形:
在当前位置放一个皇后,若当前行是最后一行,记录一个解;
若当前行不是最后一行,当前行设为下一行,当前列设为当前行的第一个待测位置;
若当前行是最后一行,当前列不是最后一列,当前列设为下一列;
若当前行是最后一行,当前列是最后一列,回溯,即清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一个待测位置;
以上返回到第2步
4)在当前位置上不满足条件的情形:
若当前列不是最后一列,当前列设为下一列,返回到第2步;
若当前列是最后一列了,回溯,即,若当前行已经是第一行了,算法退出,否则,清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一个待测位置,返回到第2步;
3.3流程图
回溯重置
图3程序流程图
4代码编写
#include
#include
#include
//方法一
staticcharQueen[8][8];//代表64个格子,空格为*,皇后为@
staticinta[8];//记录被占用的列
staticintb[15];//主对角线方向(i-j)为常数,有-7到7几种可能,为了不出现负数调整为0-15,即(i-j+7)
staticintc[15];//从对角线方向(i+j)为常数,0-无冲突,1-有冲突
staticintiQueenNum=0;//记录棋盘状态数,摆放方法数
voidqu(inti);//参数i代表行
intf1()
{
intiLine,iColumn;
//棋盘初始化,空格为*,放置皇后的地方为@
for(iLine=0;iLine<8;iLine++)
{
a[iLine]=0;//列标记初始化,表示无列冲突
for(iColumn=0;iColumn<8;iColumn++)
Queen[iLine][iColumn]='*';
}
//主、从对角线标记初始化,表示没有冲突
for(iLine=0;iLine<15;iLine++)
b[iLine]=c[iLine]=0;
qu(0);//从0行开始遍历
return0;
}
voidqu(inti)
{
intiColumn;
for(iColumn=0;iColumn<8;iColumn++)
{
if(a[iColumn]==0&&b[i-iColumn+7]==0&&c[i+iColumn]==0)//如果无冲突
{
Queen[i][iColumn]='@';//放皇后
a[iColumn]=1;//标记,下一次该列上不能放皇后
b[i-iColumn+7]=1;//标记,下一次该主对角线上不能放皇后
c[i+iColumn]=1;//标记,下一次该从对角线上不能放皇后
if(i<7)//如果行还没有遍历完,进入下一行
qu(i+1);//若递归过程中,不能找到满足第一个if条件的情况下,
//就不能进入该句继续递归,而是往下运行到后面的重置部分
else//i==7即找到一种方法,输出摆放顺序
{
//输出棋盘状态
intiLine,iColumn;
cout<<++iQueenNum<for(iLine=0;iLine<8;iLine++)
{
for(iColumn=0;iColumn<8;iColumn++)
cout<cout<}
cout<}
//如果前次的皇后放置导致后面的放置无论如何都不能满足要求,则回溯,重置
Queen[i][iColumn]='*';
a[iColumn]=0;
b[i-iColumn+7]=0;
c[i+iColumn]=0;
}
}
}
//方法二
#definemax8
intboard[max+1];
intcounter;
booltrial(intn)
{
//本函数用于判断棋盘第n行的皇后是否合法
//判断第n行是否合法要和前面的n-1行判断
//每次都是不同的行,所以同行不用判断,只需判断列和斜线
for(intp=n-1;p>=1;p--)
{
//是否有同列的棋子
if(board[p]==board[n])
returnfalse;
//如果行差等于列差,则是对角线位置
if((n-p)==abs(board[n]-board[p]))
returnfalse;
}
//如果是最后一个棋子,并且摆放合法,则打印结果
if(n==max)
{
counter++;
cout<<"方法"<"<<"\t";
for(inti=1;i<=max;i++)
cout<<"("<
cout<}
//摆放位置合法,返回true
returntrue;
}
voidsearch_position(intn)
{
//本函数用于遍历棋盘第N行的皇后所有位置
//每次都调用trial()来判定当前位置是否合法
for(intposition=1;position<=max;position++)
{
board[n]=position;
if(trial(n)&&nsearch_position(n+1);
//如果摆放位置合法,又不是最后一行,
//则寻找下一行皇后的摆放位置
}
}
intf2()
{
search_position
(1);
return0;
}
//方法三
intqueen[8][8],col[8],left[15],right[15];
intx=0,sum=0;
voidsetqueen()
{
inty,i,j;
for(y=0;y<8;y++)
{
if(col[y]&&left[x+y]&&right[x-y+7])
{
queen[x][y]=1;//1表示有皇后,不能再放
col[y]=left[x+y]=right[x-y+7]=false;
x++;
if(x==8)
{
sum++;
cout<<"这是第"<for(i=0;i<8;i++)
{
for(j=0;j<8;j++)
cout<cout<}
cout<}
elsesetqueen();
x--;
queen[x][y]=0;
col[y]=left[x+y]=right[x-y+7]=true;
}
}
}
voidf3()
{
cout<inti,j;
for(i=0;i<8;i++)
for(j=0;j<8;j++)
queen[i][j]=0;
for(i=0;i<8;i++)col[i]=true;
for(i=0;i<15;i++)left[i]=right[i]=true;
setqueen();
cout<<"皇后的摆法总数为"<}
//界面
intjiemian(intxuanze)
{
cout<cout<cout<<"八皇后问题"<cout<cout<<"=============================
"<cout<cout<<"1.方法一
"<cout<cout<<"2.方法二"<cout<cout<<"3.方法三"<cout<cout<<"请选择(1,2或3,0:
退出):
";
cin>>xuanze;
returnxuanze;
}
//主程序
intmain()
{
intxuanze(0),t;
g1:
t=jiemian(xuanze);
if(t==0)return(0);
elseif(t==1)
{
system("cls");
f1();
}
elseif(t==2)
{
system("cls");
f2();
}
elseif(t==3)
{
system("cls");
f3();
}
else
{
cout<cout<<"你的输入有误,请重新输入!
";
gotog1;
}
gotog1;
}
5程序调试
5.1调试过程与步骤
首先,三个程序一个一个的单独运行,看是否能够完成。
都能够完成所需的要求时。
就将三个程序连接在一起来运行。
5.2发现的问题
在三个程序连在一起时,计算机显示有两个错误,错误的地方是