算法与数据结构程序设计报告B08030111文档格式.docx
- 文档编号:19751917
- 上传时间:2023-01-09
- 格式:DOCX
- 页数:17
- 大小:280.91KB
算法与数据结构程序设计报告B08030111文档格式.docx
《算法与数据结构程序设计报告B08030111文档格式.docx》由会员分享,可在线阅读,更多相关《算法与数据结构程序设计报告B08030111文档格式.docx(17页珍藏版)》请在冰豆网上搜索。
intlr_orgin[16];
//lr_origin用以记录左上角往右下角数第i条斜线被哪一行的皇后占用,未被占用为0
boolRL[16];
//RL[i](i大于,小于等于)从右上角往左下角数第i条斜线上是否有皇后,true为真
intrl_orgin[16];
//rl_origin用以记录从右上角往左下角数第i条斜线被哪一行的皇后占用,未被占用为0
boolline[9];
//line[i](i大于,小于等于8)表示在第i列上是否有皇后,true为真
intline_orgin[9];
//line[i](i大于,小于等于8)表示在第i列上被哪一行的皇后占用,未被占用为0
intnum;
//用以统计解的个数
public:
Queen();
//构造函数实现数据的初始化
boolmytry(inti);
//测试第i行皇后的位置,成功返回true,失败返回false;
intSolveProblem();
//八皇后问题的解决
voidResult();
//输出结果,*8的样式显示,在皇后安放处显示一个"
Q"
};
成员变量设计:
数组location[9]用来记录一至八行各个皇后所在列的序号(location[0]不使用)。
LR[16]负责记录从左上角往右下角数,一至十六条斜线是否被占用(即实现皇后间的相互影响)。
由于回溯时需要消除已经放置的部分皇后产生的影响(即改变LR[16]数组的值),所以必须记录某一个值的改变是有哪一行的皇后引起的,否则无法知道哪些影响是该消除的,所以需要设计一个数组lr_origin[16],他的元素与LR[16]一一对应,每当某一条斜线被占用时,先将LR的某一元素的值改变,同时再在lr_origin中相应元素中记录下此改变是由某一行引起的。
数组RL[16]、rl_origin[16]道理相同,只不过是记录从右上角至左下角第i条斜线的使用情况。
此外,某一列是否被占用,被哪一行的皇后占用,也需要记录下来,所以设计bool数组line[9],和int型l数组ine_origin[9]。
有了这些,就可以记录某一皇后放置的位置,及产生的影响,同时也可以在需要的时候将相应的影响消除,便于重新放置皇后。
成员函数设计:
类的构造函数,实现数据的初始化。
一个函数负责调用其他函数解决八皇后问题。
由于八皇后问题需要不断尝试在某一行放置一个皇后,所以将这一动作也封装到一个函数中,用一个函数实现,第几行放置皇后就将几作为参数传进去。
找到一种解法后需要输出结果,将结果的输出放置到一个函数中实现。
四、详细设计
1、类的设计具体解释见“概要设计”,不再重复
类Queen()的声明:
//location[i](i大于,小于等于)表示在(i,location(i))的位置放置了一个“皇后”
boolLR[16];
//RL[i](i大于,小于等于)从右上角往左下角数第i条斜线上是否有皇后,true为真
//line[i](i大于,小于等于)表示在第i列上是否有皇后,true为真
//统计解法的数量
Queen();
boolmytry(inti);
intSolveProblem();
voidResult();
2、类Queen的构造函数主要负责将数据初始化,主要是几个数组,将其中的元素全部清零,用于统计方法数的num也置为零:
Queen:
:
Queen(){//构造函数实现数据的初始化
num=0;
intk;
for(k=0;
k<
=8;
k++)
{
location[k]=0;
line[k]=false;
line_orgin[k]=0;
}
=15;
LR[k]=false;
lr_orgin[k]=0;
RL[k]=false;
rl_orgin[k]=0;
}
}
3、八皇后问题解决函数。
初始将8个皇后都放在棋盘的外列,即数组location的值全为0。
然后从第一行开始放置皇后,每一行又从第一列开始扫描。
如果在第flag行找到了合适位置,则将其位置存储到location[flag]中,同时将相应的列、斜线置为“被占用”状态。
然后看下一行,即flag++,如果在找到了合适位置,则存入数组location[flag]中,如果没有合适的位置,那就说明上一行皇后的位置不能产生解。
此时,就应该重新调整上一行(即flag--)皇后的位置,调整过后再继续往下放置皇后,(即回溯)。
每当第8行成功放置了一个皇后就找到了一个“解”,输出之。
当全部的可能都试过以后,就求出了八皇后问题的全部解:
intQueen:
SolveProblem(){//八皇后问题的解决
intflag;
//flag作为标志位,表示当前第flag行进行尝试,看可不可以放置一个皇后
flag=1;
//从第一行开始尝试
while(flag>
0)
if(mytry(flag)==true)//如果在第flag行可以找到合适的位置放置“皇后”
{
if(flag==8){
num++;
//当前是第行皇后找到合适的位置,解法数加一,输出结果
Result();
flag--;
//标志位后移一位,即下次继续调整上一行皇后的位置
}else{
flag++;
//不是最后一行,则flag加一,继续找下一行的皇后的位置
}
}
else{
flag--;
//找不到就把flag减一,即下次调整上一行皇后的位置
returnnum;
4、在第i行放置一个皇后函数。
上一步可能是在i-1行成功放置了一个皇后,也有可能是在i+1行放置皇后失败,“回溯”到第i行。
所以,如果i+1行放置过皇后,需要将i+1行皇后产生的影响全部消除,然后再尝试在第i放置皇后。
如果当前行皇后的位置已经在第八列,则直接返回“失败”。
否则,继续尝试。
将location[i]的值不断加1(当然控制在小于等于八),检查该新的皇后的位置,该列是否被占用,所在的斜线上是否已经放置过皇后。
如果有冲突,则继续检查下一列,如果无冲突,则将皇后位置固定,将该列、所在两条斜线置为“被占用”状态,记录下被第i行占用,返回“成功”:
boolQueen:
mytry(inti){//测试第i行皇后的位置,成功返回true,失败返回false;
if((i+1)<
=8)
location[i+1]=0;
for(k=0;
if(line_orgin[k]==i||line_orgin[k]==i+1)
//i行及以后i+1行皇后放置产生的影响全部消除
line_orgin[k]=0;
line[k]=false;
}
if(rl_orgin[k]==i||rl_orgin[k]==i+1)
//i行及以后i+1行皇后放置在RL斜线上产生的影响全部消除
rl_orgin[k]=0;
RL[k]=false;
if(lr_orgin[k]==i||lr_orgin[k]==i+1)
//i行及以后i+1行皇后放置在LR斜线上产生的影响全部消除
lr_orgin[k]=0;
LR[k]=false;
if(location[i]==8)
//如果当前行皇后的位置在最后一列,则放弃往后尝试,直接返回失败
returnfalse;
for(++location[i];
location[i]<
location[i]++)
if((line[location[i]]==0)&
&
(LR[i+location[i]-1]==false)&
(RL[i-location[i]+8]==false))
//在i行,location[i]列可以放置皇后
line[location[i]]=1;
//location[i]列标记为已占用
line_orgin[location[i]]=i;
//location[i]列被i行的皇后占用
LR[i+location[i]-1]=true;
//从左上往右下角第i+location[i]-1条斜线被标记为占用
lr_orgin[i+location[i]-1]=i;
//从左上往右下角第i+location[i]-1条斜线被标记被第i行占用
RL[i-location[i]+8]=true;
//从右上往左下角第i-location[i]+8条斜线被标记为占用
rl_orgin[i-location[i]+8]=i;
//从右上往左下角第i-location[i]+8条斜线被标记被第i行占用
returntrue;
returnfalse;
5、结果输出函数,结果保存在location数组中,为了便于查看,将结果写入到文件“result.txt”中,当然,可以进行适当的格式控制,在8*8的格子中,若未放置皇后显示“_”,反之,显示“Q”:
voidQueen:
Result(){
//输出结果,8*8的样式显示,在皇后安放处显示一个"
intj,k;
ofstreamfout("
result.txt"
ios:
app);
fout<
<
"
解法"
num<
endl;
for(j=1;
j<
j++)
for(k=1;
location[j];
{fout<
_"
;
fout<
Q"
for(k=location[j]+1;
fout<
endl<
6、main函数,先生成Queen对象,调用其中的SolveProblem方法,最后显示总的解法数目,结束程序:
intmain()
{
Queenq;
cout<
共有"
q.SolveProblem()<
种解法结果保存在“result.txt”中\n"
return0;
五、测试数据及其结果分析
程序运行结果如下:
控制台显示解法总数,提示结果保存在“result.txt”中:
result.txt保存在当前文件夹下:
result.txt中显示皇后的放置方法:
六、调试过程中的问题
为了解决八皇后问题,第一反应是利用八重循环,一次在八行,八列上放置皇后,从中选择正确解,但是如果对全部位置检查,需要检查8^8次,耗时太长。
接着就考虑使用回溯法,提高效率。
回溯法有两个关键问题要考虑:
一是如何实现皇后之间的互相影响,即某一列放置皇后后,其他皇后可以“知道”避开这一列;
二是回溯以后,调整上一行皇后的位置,那么下一行“皇后”的位置必须“清零”,即从第一列开始重新变化,且消除原来放置时产生的影响。
常用方法是利用一个8*8的二维数组,共64个元素,初始值为0,然后放置皇后,每放置一个皇后以后,就将相应的行、列、斜线全部置为1,这样下次放置时,只要看相应位置上的值是否为1,若为1,则表示有冲突,考虑下一个位置。
回溯是,将相应的行、列、斜线上的1改为0即可。
这样做,比较简单,但是由于回溯的次数很多,如果采用此算法,每次需要修改很多值得大小,效率不高。
为了使程序更加简单、快捷。
可以考虑利用数个一维数组解决问题。
一个一维数组L[9]表示八列,初始值为false,某一列被占用后,就将相应列的值改为true。
同样的道理,再建立两个一维数组,LR[16]表示从左上角往右下角数的斜线,RL[16]从右上角往左下角数的斜线,初始值同样置为false,当有皇后放置在该斜线上时,将相应的值改为ture。
如果要在第i行,第j列放置一个皇后,只需保证L[j]、LR[i+j-1]、RL[i-j+8]的值都为false,如果不能保证,则表示有冲突,寻找下一个位置。
按照这个思路,我们编写程序,运行起来的不到结果。
单步调试发现,问题发生在回溯时的影响消除上。
对于影响的消除,我们一开始的设想是这样的:
从第i+1行回溯到第i行时,在调整皇后位置之前,先检查location[i+1],将L[location[i+1]]的值修改成false。
但是只有这样是不够的,程序跑不出结果。
斜线上的影响也必须消除掉,但是斜线上的几个值都被修改了,无法判断哪个值是第i+1行的皇后放置时修改的。
为了解决这一问题,设计一个辅助数组lr_origin[16],rl_origin[16],用以记录某一条斜线的值被哪一行的皇后修改。
初始时值都置为0,当数组LR、RL的元素的值被第i行修改时,将lr_origin、rl_origin的值置为i。
回溯到i行时,遍历lr_origin、rl_origin两个数组,如果某一个值等于i+1,就将相应的LR、RL数组元素的值变为false,同时lr_origin、rl_origin的这两个元素值也清零。
代码修改如下:
for(k=0;
if(lr_orgin[k]==i||lr_orgin[k]==i+1)
另一个关键问题是主循环及流程控制问题,八皇后设计到同样方法(在某一行放置皇后)的反复调用,因此,必须巧妙控制整个流程,适当的时候输出解,适当的时候回溯,最后将所有解全部求出来,又能在适当的时候跳出循环。
考虑到皇后是一行一行的放置的,从一到八,每次对其中的一行进行处理。
而第八行成功放置皇后即代表找到一个合适的解,输出之。
如果某一行找不到合适位置,需要调试上一行皇后的位置,继续寻找,所以可以设计一个标志位(即程序中的flag),初始值为1,即从第一行开始放置皇后,如果成功,flag++,往下一行寻找,如果失败,flag——,调整上一行。
每次皇后找到合适位置,判断当前flag是否为8,如果是的话,则找到一个结果,输出之,并令flag--继续寻找。
最后,当第一行(flag=1)八个位置全部考虑过以后,皇后无法往后调整,flag--,此时flag变为0,则结束程序。
编写代码如下:
flag=1;
if(mytry(flag)==true)
八皇后改进:
八皇后问题做出来以后,将其改进成N皇后问题。
N皇后问题与八皇后问题算法思想上类似,区别在于数组规模上,八皇后问题中数组大小是固定的,而在N皇后问题中是不定的,所以需要使用指针,在程序开始时动态的申请内存空间。
回溯、判断、修改值时也许要根据N的大小稍作变换。
由于N皇后问题结果可能很多(N稍大一点),所以没有将结果写到文件中,而是直接将location数组输出在屏幕上。
修改后的几段核心代码如下(全部代码另外提交):
初始时输入N并动态申请内存:
cout<
输入皇后数N:
cin>
>
n;
location=newint[n+1];
line=newbool[n+1];
line_orgin=newint[n+1];
LR=newbool[2*n];
lr_orgin=newint[2*n];
RL=newbool[2*n];
rl_orgin=newint[2*n];
算法主循环(在8皇后基础上只需将8变成n):
while(flag>
if(flag==n){
flag--;
N皇后程序运行效果截图:
初始时输入皇后数:
输入13后运行结果如下:
必须知道的是,单靠回溯法,N皇后问题在N大于15以后,结果就很难出来(需要等很久)。
因为潜在的方法数是N^N,每当N加1,需要测试的可能性会像几何级数一样迅速增加。
除了上面已经实现的n皇后的改进外,还有几点可以改进:
1、如果flag++以后,那么是不需要消除影响的,消除影响只是在flag—以后,即回溯时需要。
2、所需一维数组数量可以进一步减少,LR与lr_origin,RL与rl_origin都只需要保留后者即可,后者为0即表示未被占用,否则表示被占用。
七、程序设计总结
这次程序设计,做回溯法,一开始没有什么思路,所以事先去网上找了相关的算法思想介绍。
详细了解算法思想后,开始独立编写程序。
将一段文字描述转化成程序,中间还是有一些困难的。
主要问题出在许多细节问题没有搞清楚,就开始编程,结果导致编出来的代码运行不出结果。
后来开始单步调试,一步步跟踪,边调试边检查,最后终于发现了程序设计的大漏洞,加以弥补之后,运行,稍加修改,程序终于成功了。
由于最后程序编写好之后还剩余一段时间,就利用这段时间,在原有八皇后基础上,改写出了可以解决N皇后问题的程序。
但是由于N皇后问题自身的复杂性,以及程序本身算法限制。
导致当N大于15以后,结果就很难跑出来,这点以后有待提高。
收获:
1、对回溯算法有了更加深入的了解。
学会了用回溯法解决实际问题。
2、学会分析算法的复杂度,并改进程序,提高效率。
3、进一步掌握程序单步调试、分析、修改的方法。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算法 数据结构 程序设计 报告 B08030111