湖南大学算法分析与设计实验报告实验7蛇和梯子问题的算法设计与实现2015.06.191实验7蛇和梯子问题的算法设计与实现一、实验目的1、掌握深度优先和广度优先搜索算法的基本思想和设计方法;2、理解贪心算法的局限性;3、提高分析与解决问题的能力。二、实验内容【实验题】“蛇和梯子”是一个在N×N(0N=20)的方格棋盘上进行的游戏。(见下图)方格从1到N的平方编号。除了第1号和最后编号的方格,其它的格子都有可能有蛇或梯子存在(蛇和梯子的数量及具体位置由输入确定,它们的数量都在100之内并且蛇和梯子不能临近放置,也就是在任何了放置两者首尾的方格之间至少还有一个未放置任何东西的格子)。开始的时候玩家把他们的标志物放在1号格子中。玩家轮流以扔骰子的方式移动他们的指示物。如果一个指示物到达了一条蛇的嘴部,则把它移回蛇的尾部。如果一个指示物到达了一个梯子的底部则将它移动到梯子的顶部。如果你是一个可以自由控制骰子的高手,现在请求出你至少需要扔几次骰子才能到达标为N^2的格子。(比如在上图所示例一中,你的方案应该是走4步到达5并由梯子上升到16,再走4步到达20并由梯子上升到33,然后走3步。这样,你一共需要扔3次骰子。而在例二中,你的方案应该是连扔4个6。)三、算法的原理方法从上面的实验题中很容易看出,这个问题是不能用贪心算法解决的,因为你不能保证在这步到达一个数码比较大的格子就意味着最好的效率(即使是通过梯子到达了一个这步所能到达的最大号码的格子),也就是说,号码的大小并不能代表从这个格子到达终点所需要的步数上的多少,这就带给我们一个启发:蛇和梯子真的需要看成是两个东西来分别处理么?实际上确实是不需要的,蛇和梯子的本质就是我们经常在游戏中说的“单向传送点”,湖南大学算法分析与设计实验报告实验7蛇和梯子问题的算法设计与实现2015.06.192只不过梯子的底部是入口而顶部是出口,蛇的嘴部是入口而尾部是出口罢了,对于他们的描述完全可以选择相同的结构:structSnakeAndLadder{intfrom,to;};接下来要考虑的是解决问题的方法。贪心算法被否定之后,我们的选择可能会是搜索,对于本题所采用的搜索显然应该以广度优先的方式进行,但是稍加分析我们就会发现如果单纯地采用广度优先搜索会产生许多重复的结点,现在我们将指示物处于某格的结点简称为结点X,那么比如在例1中,第1步过后,队列中存放的结点是2,23,4,16,6,7,在第二步时,当结点2成为扩展结点时将生成结点23、4、16、6、7、8,其中只有8不存在于当前活结点队列中,即使加以判断,不把重复的结点再次加入队列中,那至少也需要对活结点队列进行搜索。实际上我们完全有更好的方法。应该意识到,采用树状结构和搜索的方法处理问题其重要的一点是利用祖先结点的差异性来对儿子结点做不同的处理。然而在本实验中,儿子结点的生成只依赖于父结点的信息而与其它祖先结点无关,所以采用树来描述这个过程其实是多余的。在走了若干步之后,对于一个特定的格子实际上只有两种状态的区分:1、在走了这些步数之后存在一种方案使指示物位于此格中;2、不存在这样的一种方案。所以我们可以用一个N×N大小的数组来描述若干步之后可以到达的格子的集合,其中每一个元素描述一个格子的状态,0表示不存在一种方案到达,1表示存在至少一种方案到达。这样,我们从表示第n步状态的数组,完全可以推出表示第n+1步状态的数组,而且在第n+1步状态的数组得到之后,表示第n步状态的数组也就不再存在利用价值了。一旦数组中表示最后一个格子的元素成为1,就表示可以通过这个步数完成任务了。比如在例1中,描述棋盘状态的数组其变化过程应该为:描述状态数组元素的内容(从表示第1个格的元素排列到表示最后一个格的元素)起始状态100000000000000000000000000000000000第1步之后010101100000000100000010000000000000第2步之后000101111111100111101111111110001000第3步之后000001111111111111101111111111111101到第3步之后,数组的最后一个元素已经变为了1,这即表明存在一种方案,使得我们扔3次骰子就可以完成任务。以下是实现此算法的主要部分代码,数组下标0——size*size-1的元素分别表示了从第1格到最后一格的状态,step记录步数,obstacle是structSnakeAndLadder的向量,描述了蛇和梯子的传送入口和出口:四、实验程序的功能模块湖南大学算法分析与设计实验报告实验7蛇和梯子问题的算法设计与实现2015.06.193typedefstructsnakeladder;//蛇与梯子的结构voidSlove();//广度优先遍历搜索最少步数五、详细代码#includeiostreamusingnamespacestd;typedefstructsnakeladder{intfrom;intto;}snakeladder;//定义蛇与梯子的类型intn,step,obNum;intgrid[1001];snakeladderobstacle[1001];voidSlove(){intl,j,k,test;intgridbak[1001];for(j=2;jn*n;j++)grid[j]=0;grid[0]=1;step=0;while(grid[n*n-1]==0){for(j=1;jn*n;j++)//而且在第n+1步状态的数组得到之后,表示第n步状态的数组也就不再存在利用价值了{gridbak[j]=grid[j];coutgridbak[j];}coutendl;for(j=0;jn*n;j++)grid[j]=0;for(j=0;jn*n-1;j++)//搜索所有的格子(最后一格不用搜索因为在过程结束前它一定为0){if(gridbak[j]==0)//若在上一步无法到达此格则跳出continue;for(k=1;k=6;k++){test=0;if(j+kn*n-1)break;for(l=0;lobNum;l++)//如果此格是一个传送入口,将传送出口的格子可达{湖南大学算法分析与设计实验报告实验7蛇和梯子问题的算法设计与实现2015.06.194if(obstacle[l].from==j+k){grid[obstacle[l].to]=1;test=1;break;}}if(test==0&&grid[j+k]==0)grid[j+k]=1;}}step++;//步数加一}coutstependl;}intmain(){intsnake,ladder,loca,value;inti,j;while(cinn)//棋盘边界{obNum=0;cinsnakeladder;//蛇数,梯子数for(i=0;isnake;i++){cinlocavalue;//输入蛇数据obstacle[loca].from=loca;obstacle[loca].to=value;obNum++;}for(j=0;jladder;j++){cinlocavalue;//输入梯子数据obstacle[loca].from=loca;obstacle[loca].to=value;obNum++;}Slove();}return0;}六、提供测试数据和相应的实验结果湖南大学算法分析与设计实验报告实验7蛇和梯子问题的算法设计与实现2015.06.195七、思考题1、试针对该问题,用深度优先的方式求解,并分析求解时存在的问题。当遍历到传送入口,直接从传送出口继续遍历知道遍历结束,否则回溯到While(jn*n-1)//只要未搜索到最后就继续搜索{for(k=1;k=6;k++){if(j+kn*n-1)//完成遍历break;test=0;//记录是否有经过传送口for(l=0;lobNum;l++)//如果此格是一个传送入口,将传送出口的格子可达{if(obstacle[l].from==j+k){j=obstacle[l].to;//直接跳到传送出口继续遍历Test++;break;}}}if(test==0)//未经过传送口,回溯j++;step++;//步数加一}