选择皇后个数后,进入子菜单,菜单中有两个模式可以选择。
2、演示程序以用户和计算机的对话方式执行,即在计算机终端上显示“提示信息”之后,由用户在键盘上输入演示程序中规定的运算命令:
相应的输入数据和运算结果显示在其后。
3、程序执行的命令包括:
1)进入主菜单。
2)选择皇后问题,输入是几皇后。
3)进入子菜单。
4)选择皇后显示模式。
5)选择结束
4、测试数据
1)N的输入为4;
2)共有2个解答。
3)分别是○●○○○○●○
○○○●●○○○
●○○○○○○●
○○●○○●○○
1.2设计平台
Windows2000以上操作系统;MicrosoftVisualC++6.0
2概要设计
问题:
N后问题
问题描述:
国际象棋中皇后可以攻击所在行,列,斜线上的每一个位置,按照此规则要在一个n*n的棋盘上放n个皇后使每一个皇后都不互相攻击
问题分析:
引入1个数组模拟棋盘上皇后的位置
引入3个工作数组
行数组[k]=1,表示第k行没有皇后
右高左低数组[k]=1,表示第k条右高左低的斜线上没有皇后
左高右低数组[k]=1,表示第k条左高右低的斜线上没有皇后
观察棋盘找到规律
同一右高左低的斜线上的方格,它们的行号和列号之和相等;
同一左高右低的斜线上的方格,它们的行号和列号只差相等;
开始时,所有行和斜线上都没有皇后,从第一列的第一行配置第一个皇后开始,在第m列的皇后位置数组[m]行放置了一个合理的皇后之后,准备考察第m+1列时,在数组行数组[],右高左低数组[],左高右低数组[]中为第m列,皇后位置数组[m]的位置设定有皇后标志
如果按此放置位置得不到结果,则把当前列中的有皇后标记改为无皇后标记。
依次类推
当在棋盘最后一列上也找到合适的位置后得到结果。
通过上面规律可以推导出结果。
2.1算法描述
回溯法——在约束条件下先序遍历,并在遍历过程中剪去那些不满足条件的分支。
使用回溯算法求解的问题特征,求解问题要分为若干步,且每一步都有几种可能的选择,而且往往在某个选择不成功时需要回头再试另外一种选择,如果到达求解目标则每一步的选择构成了问题的解,如果回头到第一步且没有新的选择则问题求解失败。
在回溯策略中,也可以通过引入一些与问题相关的信息来加快搜索解的速度。
对于皇后问题来说,由于每一行、每一列和每一个对角线,都只能放一个皇后,当一个皇后放到棋盘上后,不管它放在棋盘的什么位置,它所影响的行和列方向上的棋盘位置是固定的,因此在行、列方面没有什么信息可以利用。
但在不同的位置,在对角线方向所影响的棋盘位置数则是不同的。
可以想象,如果把一个皇后放在棋盘的某个位置后,它所影响的棋盘位置数少,那么给以后放皇后留下的余地就太大,找到解的可能性也大;反之留有余地就小,找到解的可能性也小。
2.2算法思想
算法集中在如何解决棋子之间的冲突问题。
I.判断每个棋子是否满足规则的方法可以说是如出一辙。
因此算法的整体思想是递规调用判断函数graph()。
从i行开始安置各行元素,当i>=N时输出结果.
II.具体的graph()函数的思想是:
先在第1行放上一个皇后,然后在第2行合适的位置放上一个皇后,依次类推,如果8行都放满了,说明找到了一个解,如果第好第i行的皇后后,第i+1行找不到合适的位置,这时就回到第i行,把第i行的皇后放到下一个位置,继续尝试下一行。
如此反复,知道找到所有的解。
注意,这种算法找的解可能有等价的,某些解可由别的解经过旋转棋盘得到。
2.3数据类型的定义
1)Queen表示皇后个数,.这里定义为int类型指针,是为了满足N皇后条件。
2)column表示同栏是否有皇后,定义为int类型指针。
3)rup表示右上至左下是否有皇后占用,lup表示左上至右下是否有皇后占用。
都定义为int类型指针。
4)Num表示解答的个数。
5)N表示为是皇后的个数。
6)以上几个变量都全局变量。
而且,都定义为int类型指针,是为了满足N皇后条件,可以动态生成数组。
3详细设计和实现
3.1算法流程图
图为八皇后
3.2主程序
intmain(void)
{
inti=1;
intchoice;
printf("\n\n\t**欢迎进入皇后问题**\n\n");
while(i)
{
printf("\n\t查询菜单\n");
printf("\n\t******************************************************");
printf("\n\t*No.1------------皇后问题----------------*");
printf("\n\t*No.2------------退出----------------*");
printf("\n\t******************************************************");
printf("\n\t请选择菜单号(No.0--No.1):
");
scanf("%d",&choice);
switch(choice)
{
case2:
/*退出*/
i=0;
break;
case1:
/*皇后问题*/
choice_1();
break;
default:
printf("\n\t\t\t菜单选择错误,请重新输入!
\n");
}
}
return0;
}
3.3回溯算法程序
voidgraph_1(inti)//i为行数。
即第i行皇后的位置
{
/*列索引*/
intj;
if(i>N)
{
show_1();
}
else
{
/*按列开始遍历*/
for(j=1;j<=N;j++)
{
/*如果列和两个对角线上都没有被占用的话,则占用该位置*/
if(column[j]==1&&
rup[i+j]==1&&lup[i-j+N]==1)
{
queen[i]=j;
/*标记被占用的行、列号*/
column[j]=rup[i+j]=lup[i-j+N]=0;
/*继续找下一行可以占用的位置*/
graph_1(i+1);
/*清空占用标志,寻找下一组解*/
column[j]=rup[i+j]=lup[i-j+N]=1;
}
}
}
}
4调试与操作说明
4.1调试情况
这次的课程设计的代码不多,所以等有了解题思路后,把代码都写上后难免会有很多错误。
当第一次把整个程序写好后运行,出现了很多错误。
不过经过一点点的改正,错误也慢慢地变少。
这也说明做事要认真,尤其做计算机这方面工作的时候,因为计算机不容许不一点点的错误,有了一点小错误和有一个大错误在计算机看来都是一样的,都不会得不到结果。
有些小错误,比如说少了个分号,变量忘了定义,数据溢出等都是些小错误,但也不能松懈。
因为要注意的地方很多,经过多次尝试,问题也就自然而然的解决了,而且以后遇到这方面的问题都会觉得比较得心应手。
4.2操作说明
生成界面如图
图4.1生成界面
图4.2生成界面
图4.3视图输出界面
图4.4皇后列数界面
当程序运行的时候会出现如上图所示的提示,要求使用者选择输入菜单1或2。
输入1后,再输入是几皇后问题。
输入后进入子菜单,选择哪一种输出。
输入1后。
输出是解答。
5设计总结
就编写的程序而言,虽然能达到预期的结果,但在运行时所需的时间比较长,而且总体结构还不够简洁,不太容易去理解。
许多问题还需要继续研究,许多技术还需要更多的改进。
去网上看了些资料,只是对大概的知识有了点了解,但还是很难着手于写代码,后来就按照老师说的,先搞清楚原理,再考虑如何去实现!
后来又去上网查看相关资料,总算有头绪了。
但在调试过程中,还是遇到了很多困难,后来通过了很多同的帮助才把问题解决了。
通过这次的课程设计,让我了解了八皇后这一经典的问题。
同时让我更好地掌握了回溯法以及一维数组等等知识,以及一些书本上没有的东西,这对我以后的学习生涯以及将来步入社会起到很大的帮助。
这次课程设计虽然花了我不多时间和精力,这次课程设计也提醒我以前知识的匮乏,它给我敲响了警钟,让我意识到自己基础的不扎实.当然这次实验还是有很多问题的。
在编写代码时,我希望能随机选择一数X(1~92)后,能输出该种情况所对应的八个皇后的摆放方式和每个皇后所在的位置,但想了好久,就是无法实现。
而且,当92种情况都输出时,前面的几十种情况无法看到,要想让摆放皇后的图形和所在具体的位置一起输出,就得修改程序让使她们一个一个地输出,这样显然比较麻烦。
针对八皇后这个课题,也许表层只局限于对八个皇后的摆放,但还可以对更多的情况进行探讨分析,比如九皇后,十皇后等等。
在报告正文中已经多次提到关于N皇后的设计方法,但只是一带而过,有些问题很难通过一个报告设计就轻而易举的得到解决,还需要花费更多的时间。
也许随着皇后个数的增多,程序运行的时间将变得很长,我们能否将运行的时间缩短呢?
参考文献
1周云静.数据结构习题解析与上机指导.北京:
冶金工业出版社,2004
2严蔚敏,吴伟民.数据结构.北京:
清华大学出版社,1997
附录
源程序附下:
Main.c
#include"queen.h"
intmain(void)
{
inti=1;
intchoice;
printf("\n\n\t**欢迎进入皇后问题**\n\n");
while(i)
{
printf("\n\t查询菜单\n");
printf("\n\t******************************************************");
printf("\n\t*No.1------------皇后问题----------------*");
printf("\n\t*No.2------------退出----------------*");
printf("\n\t******************************************************");
printf("\n\t请选择菜单号(No.0--No.1):
");
scanf("%d",&choice);
switch(choice)
{
case2:
/*退出*/
i=0;
break;
case1:
/*皇后问题*/
choice_1();
break;
default:
printf("\n\t\t\t菜单选择错误,请重新输入!
\n");
}
}
return0;
}
queen.h
#include
#include
int*column=0;//同栏是否有皇后
int*rup=0;//右上至左下是否有皇后占用
int*lup=0;//左上至右下是否有皇后占用
int*queen=0;
intnum;////最后的答案记录
intN;//皇后个数
voidinsert()
{
column=(int*)malloc(sizeof(int)*(N+1));
rup=(int*)malloc(sizeof(int)*(N*2+1));
lup=(int*)malloc(sizeof(int)*(N*2+1));
queen=(int*)malloc(sizeof(int)*(N+1));
printf("\n\n\t▁▂▃▄欢迎进入%d皇后问题▄▃▂▁\n\n",N);
}
/*释放空间*/
voiddelet()
{
free(column);
free(rup);
free(lup);
free(queen);
}
voidinit()
{
inti;
for(i=1;i<=N;i++)
column[i]=1;//1为未用,0为已用
for(i=1;i<=2*N;i++)
rup[i]=lup[i]=1;//1为未用,0为已用//
for(i=0;i<=N;i++)
queen[i]=0;
num=0;
}
voidanswer()
{
printf("\n\t==%d皇后问题==",N);
if(num==0)
printf("\n\t==无解答==\n\n");
else
printf("\n\t==共有%d解答==\n\n",num);
}
/*图形显示*/
voidshow_1()
{
inti;
intx,y;
printf("\n解答%d▄▃▂▁\n",++num);
for(y=1;y<=N;y++)
{
for(x=1;x<=N;x++)
{
if(queen[y]==x)
{
printf("●");
}
else
{
printf("○");
}
}
printf("\n");
}
}
/*行号显示*/
voidshow_2()
{
inti;
printf("\n解答%d▄▃▂▁\n",++num);
for(i=1;i<=N;i++)
printf("%d",queen[i]);
printf("\n");
}
/*八皇后算法实现函数*/
voidgraph_1(inti)//i为行数。
即第i行皇后的位置
{
/*列索引*/
intj;
if(i>N)
{
show_1();
}
else
{
/*按列开始遍历*/
for(j=1;j<=N;j++)
{
/*如果列和两个对角线上都没有被占用的话,则占用该位置*/
if(column[j]==1&&
rup[i+j]==1&&lup[i-j+N]==1)
{
queen[i]=j;
/*标记被占用的行、列号*/
column[j]=rup[i+j]=lup[i-j+N]=0;
/*继续找下一行可以占用的位置*/
graph_1(i+1);
/*清空占用标志,寻找下一组解*/
column[j]=rup[i+j]=lup[i-j+N]=1;
}
}
}
}
voidgraph_2(inti)//i为行数。
即第i行皇后的位置
{
/*列索引*/
intj;
if(i>N)
{
show_2();
}
else
{
/*按列开始遍历*/
for(j=1;j<=N;j++)
{
/*如果列和两个对角线上都没有被占用的话,则占用该位置*/
if(column[j]==1&&
rup[i+j]==1&&lup[i-j+N]==1)
{
queen[i]=j;
/*标记被占用的行、列号*/
column[j]=rup[i+j]=lup[i-j+N]=0;
/*继续找下一行可以占用的位置*/
graph_2(i+1);
/*清空占用标志,寻找下一组解*/
column[j]=rup[i+j]=lup[i-j+N]=1;
}
}
}
}
voidchoice_1()
{
intchoice;
printf("\n\t请输入皇后个数:
\n");
printf("\t(1~10)");
scanf("%d",&N);
if(N<1)
printf("\n\n\t\t\t个数选择错误,请重新输入!
\n\n");
else
if(N>10)
printf("\n\n\t\t\t个数选择错误,请重新输入!
\n\n");
else
{
insert();
/*先初始化棋盘的记录*/
init();
printf("\n\t查询菜单\n");
printf("\n\t******************************************************");
printf("\n\t*No.1--------视图矩阵形式显示皇后的位置*");
printf("\n\t*No.2--------每一行皇后放置的列数的情况*");
printf("\n\t******************************************************");
printf("\n\t请选择菜单号(No.1--No.2):
");
scanf("%d",&choice);
switch(choice)
{
case1:
/*视图实现函数*/
graph_1
(1);
answer();
break;
case2:
/*列数实现函数*/
graph_2
(1);
answer();
break;
default:
printf("\n\t\t\t菜单选择错误,请重新输入!
\n");
}
delet();
}
}