课程设计报告.docx
- 文档编号:30741892
- 上传时间:2023-08-20
- 格式:DOCX
- 页数:21
- 大小:68.20KB
课程设计报告.docx
《课程设计报告.docx》由会员分享,可在线阅读,更多相关《课程设计报告.docx(21页珍藏版)》请在冰豆网上搜索。
课程设计报告
合肥学院
计算机科学与技术系
课程设计报告
2011~2012学年第二学期
课程
数据结构与算法
课程设计名称
阴风习习的大楼
学生姓名
常新亚
学号
100401025
专业班级
10计本一班
指导教师
李红陈艳平
2012年6月13日
数据结构与算法课程设计报告
一.题目:
佳佳在一个实验室工作,他每天晚上都要一个人工作到很晚很晚,即使他再努力,也不能避免要上厕所的命运。
佳佳是个胆小的孩子,黑夜里上厕所却是一件需要胆量的事,所以佳佳想让你帮他计算一下去厕所的路。
实验室所在的大楼的地形是一个n*n的正方形,每个点或者有路,或者是墙。
这里有一些地方是比较可怕的(比如会有呜呜的风声,或者那里没有灯光),每一个这种地方有一个可怕值k,佳佳经过这里的时候会在心里留下k的阴影。
佳佳有一个心理承受能力s,当累计的k值超过或等于s时,佳佳会落荒而逃奔回实验室……现在请你帮佳佳看看,他能不能安全到达厕所呢?
实验室在地图的左上角,而厕所在地图的右下角。
设计程序完成如下要求:
(1)输入:
多组测试数据;每组数据输入的第一行是两个数n,s(n<=10,s<=10^9);接下来n行,每行有n个数,分别代表:
0这个位置是空地(实验室和厕所都是0),-1这个位置是墙,其他正整数:
这个位置的可怕程度。
(2)输出:
对于每组数据,输出Yes表示佳佳能安全到达厕所,否则输出No。
(3)所设计的数据结构应尽可能节省存储空间。
(4)程序的运行时间应尽可能少。
二.问题分析:
根据题目要求我们可以将阴风习习的大楼问题(佳佳上厕所问题)抽象为如下一个查找矩阵中左上角到右下角带权最短路径模型。
0
5
8
9
9
9
-1
3
3
9
8
7
2
-1
6
0
0
-1
-1
-1
7
6
9
3
4
3
-1
-1
7
7
6
0
3
0
9
0
图1
厕所
把大楼抽象成上图所示的表格形状,若坐标设为(1,1)~(n,n),这样实验室的坐标就是(1,1),而厕所的坐标就是(n,n)。
图中每个单元格中的数代表这一点的恐怖值,-1代表这点是墙,不可通过。
通过这样的抽象,我们的任务就是完成计算从左上角(1,1)开始遍历到达右下角(n,n)的最短带权路径长度。
实现模型的要求需要解决一下一个问题:
1.怎样存储这个模型,即模型的数据结构。
2.怎样对模型进行遍历,每一点邻接位置有4个。
3.怎样判断当前遍历到终点的路径是最小的带权路径。
4.怎样解决每个点访问权限的问题。
即一个路径对每个点只能访问一次,一个点可以被多个路径访问。
5.判断找到的最短路径与心理承受值大小。
本程序的关键问题和难点在于如何遍历找出带权最短路径。
如上图1,建立如下坐标系:
0
1
2
3
4
5
0
0
5
8
9
9
9
1
-1
3
3
9
8
7
2
2
-1
6
0
0
-1
3
-1
-1
7
6
9
3
4
4
3
-1
-1
7
7
5
6
0
3
0
9
0
图2
这样我们的目标就是计算出从(0,0)到达(5,5)点的最短路程。
当然我们大可不必计算出最短的,因为要求只需要判断是否有权值和小于等于心理承受能力的一个路径。
但我们能找出最短路径也就可以稍加修改的完成程序要求了。
除了边上和顶角的点,每个点向后都有四个选择,去掉来到该点的一个选择,还有三个选择;边上的点有两个选择;顶角的只有一种选择。
为了避免边角的判断,我们可以将表格扩大一点。
这在详细设计中具体介绍。
从(0,0)点开始我们优先选择下边的邻接位置,次之访问右边的邻接位置。
当到达下边的(1,0)时,用相同大小的数组记录到这个路径当前位置到达起点的累积权值,比如(0,1)记录为5,因为(1,0)为墙,不记录在内。
然后是(1,1)……直到找到终点(5,5)或是路径是死路,就返回到上个位置,并查找上个位置的其他邻接位置。
也就是深度优先搜索遍历。
这样当我们走完所以起点到终点的路径后,终点位置就记录了最短路径的权值了。
三.概要设计和数据结构选择
1.上面用一个二维矩阵来表示大楼情况,所以现在就定义以下数据结构来存储矩阵:
structGo_to_stool
{
intn;//大楼的边长
longints;//佳佳的心理承受能力
longintbuilding[maxlen+2][maxlen+2];//大楼的存储结构
};
2.同时为了记录访问路径中个点的访问情况和路径累积权值,还需要定义如下的数据结构来存储:
structvisite//访问标志结构
{
intswap[maxlen+2][maxlen+2];//是否交换过标志
inttag[maxlen+2][maxlen+2];//访问标志
longintvisited[maxlen][maxlen];//存放各点到终点的最短路径
};
3.流程图
调用完
N
Y
图3
四.算法思想
程序的核心是遍历这个二维矩阵,得到带权的最短路径,主要算法思想如下:
1.采用递归的深度优先搜索遍历路径。
2.首先遍历一个点的下侧邻接点,然后遍历其右侧邻接点,其次是左侧,再者是上侧。
3.每个邻接点有四个属性:
A.该点的权值,即大楼该位置的恐怖值或墙。
B.该点的访问标志。
C.该点到起点的累积最小权值。
D.该点权值交换标志。
4.从左上角的起点(0,0)开始深度优先遍历,每次遍历到一个点时判断该点是否为墙并且没有被访问过,然后判断该点的累积权值是否被交换过,若没有则将该路径上一个点的累积权值加上该点的权值作为该点的累积权值,这样就记录了该点到起点的累积路径权值了。
这样遍历到终点就会记录该路径上个点到起点的累积权值,不过这不一定是最小的。
根据递归函数的性质,将退回上个节点遍历其他路径,这样,当一个点被重复遍历到时则将该点的累积权值交换为最小路径的累积权值。
这样当遍历完所有路径后,就记录了每个点到达起点的最小路径权值了。
我们想要的终点到起点的最小路径权值和也就出来了。
同时也知道了最短路径。
0
0
5
5+0
-1
^
3
3+5
3
3+8
-1
^
6
6+11
7
7+17
6
6+24
9
9+30
-1
^
7
7+39
9
9+46
0
55
图4
如图4,是一条路径遍历的结果,遍历到终点时便记录了这条路径的带权长度。
五.详细设计
1.定义大楼类型和访问存储类型:
structGo_to_stool//大楼类型
{
intn;//大楼的边长
longints;//佳佳的心理承受能力
longintbuilding[maxlen+2][maxlen+2];//大楼的存储结构
};
structvisite//访问标志结构
{
intswap[maxlen+2][maxlen+2];//是否交换过标志
inttag[maxlen+2][maxlen+2];//访问标志
longintvisited[maxlen][maxlen];//存放各点到终点的最短路径
};
将存储大楼数据的数组扩大一圈可以解决需要考虑边缘位置只有2或1邻接位置,简化计算。
2.初始化大楼属性和输入大楼的属性数据。
for(i=0;i for(j=0;j W->building[i][j]=-1; 采用两个for循环,将大楼的结构初始化为墙,避免后面访问时出现未知数据的情况。 这一个初始化将外围全部赋值为-1,即是墙,这样在访问边位置的时候就方便了。 for(i=1;i<=W->n;i++) for(j=1;j<=W->n;j++) scanf("%ld",&W->building[i][j]);//得到大楼结构 同样采用两个for循环的结构,输入大楼的数据,注意这里i从1开始,到给定的一个边长结束,这个边长最大为10,而我们定义的数据结构给出最大为12,这样这个二维数组的第0行,第0列,第maxlen+1行,第maxlen+1列都初始化为墙了。 后面访问时就可以把所有点的邻接位置归结为3种了。 2.遍历算法举例说明 为了简化问题说明,我们用一个4*4的矩阵来表示大楼的结构,即大楼边长为4。 -1 -1 -1 -1 -1 -1 -1 0 3 5 0 -1 -1 7 7 6 -1 -1 -1 -1 0 3 7 -1 -1 -1 5 9 0 -1 -1 -1 -1 -1 -1 -1 图5 A.递归函数简介: 首先介绍下程序的核心递归函数,方便后面举例讲解。 这个递归函数是从起点开始(程序需要也可以从任何点开始)深度优先递归访问它的下邻接位置,其次递归访问右邻接位置,再者是上,最后是左邻接位置。 访问需要一定条件,必须访问到的位置不能是墙,无已访问标志。 函数中用到4个数组,一个是building[]用于存储大楼结构的,一个人tag[]用于标志某个点是否访问过。 一个是swap[]用于标志visited[]中累积权值是否被交换过,一个是visited[]用于记录各点到起点的权值累积。 B.如图5,边缘为的-1都是初始化为墙的。 首先从起点(1,1)开始访问,(1,1)有四个邻接位置但是其中有2个是墙,则(1,1)点有两种走法,首先走下面。 if(W->building[i+1][j]! =-1&&V->tag[i+1][j]! =1&&V->swap[i+1][j]! =1) 通过if语句判断它的下邻接位置是否是墙、有没有被访问过、有没有交换记录。 如都是否,则V->visited[i+1][j]=V->visited[i][j]+W->building[i+1][j]; 记录下下邻接位置的累积权值,然后标记为访问过、交换过: V->tag[i+1][j]=1;V->swap[i+1][j]=1;然后递归的访问(2,1)的下邻接位置。 同上面判断它的邻接位置(3,1)是墙,这结束该次访问,即递归函数返回到上一次调用中;接着优先访问右邻接位置,即(2,2)。 这样递归的访问到终点后,终点就记录了本次路径的累积权值,但这不一定是最小的。 根据递归函数性质,函数会返回到上一个位置,查找其它的邻接位置,形成其它路径。 当其它路径经过和本路径同一点时,取最小的权值累积。 V->visited[i-1][j]=MIN(V->visited[i-1][j],V->visited[i][j]+W->building[i-1][j]); 当递归函数完成以后,所有可能到达的路径就都遍历过了,这样各点就都记录下到达起点的最短距离了。 0 3 8 8 7 10 14 10 13 20 15 22 20 图6 如上图,当递归函数结束后,我们可以看到终点就保存着最小的累积权值。 这样我们从终点开始向起点走,每步选择最小的权值累积的位置走,就可以得到最短路径了。 六.上机调试 1.语法错误及修改: 由于本算法使用了递归的思想进行遍历,所以程序可以相对来说得到了简化,语句有所减少。 所以出现的语法问题主要在于子函数和变量的定义,括号的配对,关键字和函数名称的书写,以及一些库函数的规范使用。 这些问题均可以根据编译器的警告提示,对应的将其解决。 2.逻辑问题修改和调整: 递归的方法虽然简化了程序,但是增加了对算法过程的认识难度。 在本程序中,递归函数需要多次的调用自己。 多则达到千万次。 所以对递归函数中的条件需要控制的很好。 递归函数用的是深度优先的遍历思想,先深度遍历下位置,再则是右位置,然后是上和左。 为什么这样考虑呢? 因为从题目我们可以看出,起点在终点的左上方,这样优先考虑右下方向就可以避免很多不必要的多余路径。 还有一个修改就是当遍历到终点的时候就返回,这样就减少了一部分的不必要的访问。 其中还有一个比较关键的问题就是我们每访问一个节点时都会标志这个点被访问过了,以后就不准在访问了,这样虽然避免了路径回头的问题,但是这样第二条路径或其他路径就不能访问这个点了,这样就产生了错误。 为了解决这个问题,我们每当一个点的所有邻接位置都访问完的时候就释放掉这个点的访问权限。 这样不同的路径就可以重复访问一个点,并且在一条路径中不会重复的访问某一点了。 七.测试用例、结果及算法性能分析 1.测试数据n=6,s=37 0 5 8 9 9 9 -1 3 3 9 8 7 2 -1 6 0 0 -1 -1 -1 7 6 9 3 4 3 -1 -1 7 7 6 0 3 0 9 0 图7 3.测试结果 图8 4.时间、空间性能分析: 本算法中用到了4个二维数组,长度是maxlen+2,当然实际用到的与大楼的边长有关,总体的空间复杂度为O(n^2)。 相对来说是比较好的,因为题目要求中就要输入n^2个信息。 但是相对来说时间性能比较差,因为本算法为了找出最短的路径(其实可以不必找到最短路径,本人对程序功能进行了扩展),需要遍历出几乎所有可能路径后才可以等到最短路径。 这样时间性能就与给出的大楼的结构中路径个数有关。 对于6*6的矩阵结构最坏情况需要5695845次调用递归函数。 4*4的最坏需要395次调用递归函数。 由于能力所限,本人无法对该算法进行时间性能的优化。 八.用户使用说明 本程序运行过程时带有提示性语句。 由于本程序可以对边长不同的大楼(n<=10)结构进行遍历,所以运行需要根据提示输入边长和佳佳的心里承受能力,以空格分隔。 回车,然后需要输入大楼的结构,数据间以空格分开,-1代表该位置是墙,正整数代表该点的恐怖值,每一行以回车分隔。 回车,输入完成有提示信息。 然后是程序计算出的最短路径累积和程序时间复杂度及佳佳是否可以安全到达厕所的信息。 九.参考文献 [1]王昆仑,李红.数据结构与算法.北京: 中国铁道出版社,2006年5月。 十.附录 #include #definemaxlen10 structGo_to_stool { intn;//大楼的边长 longints;//佳佳的心理承受能力 longintbuilding[maxlen+2][maxlen+2];//大楼的存储结构 }WC; structvisite//访问标志结构 { intswap[maxlen+2][maxlen+2];//是否交换过标志 inttag[maxlen+2][maxlen+2];//访问标志 longintvisited[maxlen][maxlen];//存放各点到终点的最短路径 }VT; structGo_to_stool*W=&WC; structvisite*V=&VT; staticlongintx=0; voidinput(void)//输入游戏模型 { inta=1,n,i,j; longints; while(a) { printf("输入边长,心理承受能力: "); scanf("%d%ld",&n,&s); if(n>0&&n<=maxlen&&s>0&&s<1e9) { W->n=n;//得到大楼边长 W->s=s; a=0; printf("\n"); } else printf("输入有误\n"); } for(i=0;i for(j=0;j W->building[i][j]=-1; printf("输入大楼结构\n"); for(i=1;i<=W->n;i++) for(j=1;j<=W->n;j++) scanf("%ld",&W->building[i][j]);//得到大楼结构 printf("输入完成"); } longintMIN(longinta,longintb) { if(a returna; else returnb; } voiddfs_building(inti,intj)//遍历整个大楼并计算各个点到终点的最短路径 { x++; { if(W->building[i+1][j]! =-1&&V->tag[i+1][j]! =1&&V->swap[i+1][j]! =1)//优先遍历下邻接位置 if((i+1)! =W->n||(j)! =W->n)//不是终点 { V->visited[i+1][j]=V->visited[i][j]+W->building[i+1][j]; V->tag[i+1][j]=1;//标志已访问 V->swap[i+1][j]=1;//标志可交换 dfs_building(i+1,j);//递归 } else//是终点 { V->visited[i+1][j]=MIN(V->visited[i][j],V->visited[i+1][j]);//取得最短路径累积 V->tag[i][j]=0;//释放访问权限 return;//结束 } elseif(W->building[i+1][j]! =-1&&V->tag[i+1][j]! =1&&V->swap[i+1][j]==1) //可交换时 if((i+1)! =W->n||(j)! =W->n) {V->visited[i+1][j]=MIN(V->visited[i+1][j],V->visited[i][j]+W->building[i+1][j]); V->tag[i+1][j]=1; dfs_building(i+1,j); } else { V->visited[i+1][j]=MIN(V->visited[i][j],V->visited[i+1][j]); V->tag[i][j]=0; return; } if(W->building[i][j+1]! =-1&&V->tag[i][j+1]! =1&&V->swap[i][j+1]! =1)//访问右侧邻接位置 if((i)! =W->n||(j+1)! =W->n) { V->visited[i][j+1]=V->visited[i][j]+W->building[i][j+1]; V->tag[i][j+1]=1; V->swap[i][j+1]=1; dfs_building(i,j+1); } else { V->visited[i][j+1]=MIN(V->visited[i][j],V->visited[i][j+1]); V->tag[i][j]=0; return; } elseif(W->building[i][j+1]! =-1&&V->tag[i][j+1]! =1&&V->swap[i][j+1]==1) if((i)! =W->n||(j+1)! =W->n) {V->visited[i][j+1]=MIN(V->visited[i][j+1],V->visited[i][j]+W->building[i][j+1]); V->tag[i][j+1]=1; dfs_building(i,j+1); } else { V->visited[i][j+1]=MIN(V->visited[i][j],V->visited[i][j+1]); V->tag[i][j]=0; return; } if(W->building[i-1][j]! =-1&&V->tag[i-1][j]! =1&&V->swap[i-1][j]! =1)//不是墙并且未访问过 if((i-1)! =W->n||j! =W->n) { V->visited[i-1][j]=V->visited[i][j]+W->building[i-1][j]; //取最短路径 V->tag[i-1][j]=1; V->swap[i-1][j]=1; dfs_building(i-1,j);//访问邻接位置 } else { V->visited[i-1][j]=MIN(V->visited[i][j],V->visited[i-1][j]); V->tag[i][j]=0; return; } elseif(W->building[i-1][j]! =-1&&V->tag[i-1][j]! =1&&V->swap[i-1][j]==1)//不是墙且被访问过 if((i-1)! =W->n||j! =W->n) {V->visited[i-1][j]=MIN(V->visited[i-1][j],V->visited[i][j]+W->building[i-1][j]);//取最短路径 V->tag[i-1][j]=1; dfs_building(i-1,j);//访问邻接位置 } else { V->visited[i-1][j]=MIN(V->visited[i][j],V->visited[i-1][j]); V->tag[i][j]=0; return; } if(W->building[i][j-1]! =-1&&V->tag[i][j-1]! =1&&V->swap[i][j-1]! =1) if(i! =W->n||(j-1)! =W->n) { V->visited[i][j-1]=V->visited[i][j]+W->building[i][j-1]; //取最短路径 V->tag[i]
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 课程设计 报告
![提示](https://static.bdocx.com/images/bang_tan.gif)