第1章 迷宫算法
【小白学游戏常用算法】一、随机迷宫算法
【⼩⽩学游戏常⽤算法】⼀、随机迷宫算法 现在的很多游戏中的地图⼀般采⽤格⼦的⽅式,虽然在表⾯地图上⽆法看到实际的格⼦,但是在地图的结构中专门有⼀个逻辑层,这个层和地图⼤⼩相等,划出很多⼩的格⼦,然后在可以通过的地⽅使⽤0表⽰,在有障碍的且不能通过的地⽅⽤1或者其他数字表⽰(如图所⽰)。
有了这个逻辑层之后,实际上⾃动寻路就转换成了如何在⼀个⼆维数组中找出⼀条从逻辑值为0的地点移动到⽬标的路径。
在寻路之前,我们⾸先要随机⽣成这些地图。
游戏中地图 ⼆维数组逻辑层 本质上,地图的障碍逻辑层是由⼀个⼆维数组保存的。
障碍标记在⼆维数组中的数据值以0或者1表⽰,我们⾸先需要做的就是随机产⽣这样的⼆维数组。
当然,最简单的办法就是循环这个⼆维数组,然后在每⼀个位置随机地产⽣0或者1,但是这种算法产⽣的图形⽐较难看,并且不⼀定保证图中的任意两点可以相连通。
在随机⽣成的迷宫中要求任意两点,都可以找到⼀条路径相通,所以在图论中可以认为迷宫就是⼀个连通图。
产⽣连通图的常见⽅法有克鲁斯卡尔和普利姆算法,这⾥我们以普利姆算法为例实现⼀下,使⽤普利姆算法产⽣的迷宫⽐较⾃然和随机。
(1)如上图所⽰为⼀个6x6的迷宫,先假设迷宫中所有的通路都是完全封闭的,黄⾊的格⼦表⽰可以通过,⿊⾊的格⼦表⽰墙壁或者障碍不能通过。
(2)随机选择⼀个黄⾊的格⼦作为当前正在访问的格⼦,同时把该格⼦放⼊⼀个已经访问的列表中。
(3)循环以下操作,直到所有的格⼦都被访问到。
1.得到当前访问格⼦的四周(上下左右)的格⼦,在这些格⼦中随机选择⼀个没有在访问列表中的格⼦,如果找到,则把该格⼦和当前访问的格⼦中间的墙打通(置为0),把该格⼦作为当前访问的格⼦,并放⼊访问列表。
2.如果周围所有的格⼦都已经访问过,则从已访问的列表中,随机选取⼀个作为当前访问的格⼦。
通过以上的迷宫⽣成算法,可以⽣成⼀个⾃然随机的迷宫、 下⾯使⽤代码实现⼀个R⾏N列⼤⼩的随机迷宫,R⾏表⽰的是刚开始空⽩格⼦的⾏数,⽽格⼦之间还有墙壁和障碍物,所以最终产⽣的⼆维数组⼤⼩实际为2R+1 * 2N+11//产⽣随机迷宫2 primMaze:function(r,c)3 {4//初始化数组5function init(r,c)6 {7var a = new Array(2*r+1);8//全部置19for(var i=0,len=a.length;i<len;i++)10 {11var cols = 2*c+1;12 a[i]= new Array(cols);13 ArrayUtil.fillWith(a[i],1);14 }15//中间格⼦为016for(var i=0;i<r;i++)17for(var j=0;j<c;j++)18 {19 a[2*i+1][2*j+1] = 0;20 }21return a;22 }23//处理数组,产⽣最终的数组24function process(arr)25 {26//acc存放已访问队列,noacc存放没有访问队列27var acc = [],noacc = [];28var r = arr.length>>1,c=arr[0].length>>1;29var count = r*c;30for(var i=0;i<count;i++){noacc[i]=0;}31//定义空单元上下左右偏移32var offs=[-c,c,-1,1],offR=[-1,1,0,0],offC=[0,0,-1,1];33//随机从noacc取出⼀个位置34var pos = MathUtil.randInt(count);35 noacc[pos]=1;36 acc.push(pos);37while(acc.length<count)38 {39var ls = -1,offPos = -1;40 offPos = -1;41//找出pos位置在⼆维数组中的坐标42var pr = pos/c|0,pc=pos%c,co=0,o=0;43//随机取上下左右四个单元44while(++co<5)45 {46 o = MathUtil.randInt(0,5);47 ls =offs[o]+pos;48var tpr = pr+offR[o];49var tpc = pc+offC[o];50if(tpr>=0&&tpc>=0&&tpr<=r-1&&tpc<=c-1&&noacc[ls]==0){ offPos = o;break;}51 }52if(offPos<0)53 {5455 pos = acc[MathUtil.randInt(acc.length)];56 }57else58 {59 pr = 2*pr+1;60 pc = 2*pc+1;61//相邻空单元中间的位置置062 arr[pr+offR[offPos]][pc+offC[offPos]]=0;63 pos = ls;64 noacc[pos] = 1;65 acc.push(pos);66 }67 }68 }69var a = init(r,c);70 process(a);71return a;72 }利⽤上⾯的算法我们就可以实现⼀个类似于下⾯的随机迷宫了。
迷宫算法(经典广度优先搜索算法)
经典广度优先搜索算法,用parentx,parenty存储上一步的位置,规范化使用队列和栈。
迷宫暂定为8*6,动态生成,四周为一圈障碍,出口坐标为(8,6)。
坐标的定义类似图形编程的屏幕坐标,横向为x分量,垂直为y分量,左上角为原点。
可以向8个方向试探。
源代码(TC下编译运行通过):#include <stdio.h>#include <stdlib.h>#define Status int#define OK 1#define ERROR 0#define OVERFLOW -2#define TRUE 1#define FALSE 0#define ROW 8 /*行列可自定*/#define COLUM 10 /*行列可自定*/#define OUT 8#define STEPPED 2#define MAXQSIZE 100int maze[8][10]/*6行8列*/={{1,1,1,1,1,1,1,1,1,1},{1,0,1,1,1,1,0,0,0,1},{1,0,1,0,0,1,1,0,0,1},{1,0,0,0,1,0,1,0,0,1},{1,1,1,0,1,0,1,0,0,1},{1,0,0,0,0,0,0,0,0,1},{1,0,0,0,0,0,0,1,OUT,1},{1,1,1,1,1,1,1,1,1,1}};/*此处只是给一个初始化的例子,可以删去,后面的代码可以动态生成迷宫*/void CreateRandomMaze()/*随机生成迷宫(可能产生走不通的迷宫)*/{int i,j;srand((int)time());/*设置随机数种子,产生真随机数*/for(i=0;i<ROW;i++){for(j=0;j<COLUM;j++){maze[i][j]=rand()%2;/*产生0~1的随机数*/}}for(i=0;i<COLUM;i++){maze[0][i]=1;maze[ROW-1][i]=1;}for(i=0;i<ROW;i++){maze[i][0]=1;maze[i][COLUM-1]=1;}maze[1][1]=0;maze[ROW-2][COLUM-2]=OUT;/*设置出口*/}void printMaze()/*打印迷宫*/{int i,j;/*clrscr();*//*这个是turbo C的清屏函数,可以替代或忽略*/for(i=0;i<ROW;i++){for(j=0;j<COLUM;j++){printf(" %d",maze[i][j]);}printf("\n");}/*delay(1000);*/}/*********************队列(非循环顺序队列)************************/typedef struct{int x,y;int parentx,parenty; /*记录上一个节点即路径中前驱节点,方便最后输出*/ }QNode,*QueuePtr;typedef QNode ElemType;typedef struct SqQueue{ElemType * base;int front;int rear;}SqQueue;/*队列结构体*/Status InitQueue(SqQueue *Q)/*初始化队列*/{Q->base = (ElemType *)malloc(MAXQSIZE *sizeof(ElemType)); if(!Q->base){exit(OVERFLOW);}Q->front = Q->rear = 0;return OK;}int QueueLength(SqQueue Q){return Q.rear - Q.front;}Status QueueEmpty(SqQueue Q){if(Q.front==Q.rear){return TRUE;}return FALSE;}Status GetHead(SqQueue Q,ElemType *e){if(Q.front==Q.rear){return ERROR;}*e = Q.base[Q.front];return OK;}Status EnQueue(SqQueue *Q,ElemType e){if(Q->rear>MAXQSIZE-1)/*队列满*/{return ERROR;}Q->base[Q->rear]=e;(Q->rear)++;return OK;}Status DeQueue(SqQueue *Q,ElemType *e) {if(Q->front == Q->rear)/*队列空*/{return ERROR;}*e = Q->base[Q->front];(Q->front)++;return OK;}/*********************栈**********************/ typedef struct{int top,base,count;ElemType container[100];/*ROW*COLUM];*/ }SqStack;Status InitStack(SqStack *s){s->top=-1;s->base=0;s->count=0;}Status StackEmpty(SqStack s){if(s.count!=0){return FALSE;}return TRUE;}Status Push(SqStack *s,ElemType e){s->top++;s->container[s->top]=e;s->count++;return OK;}Status Pop(SqStack *s,ElemType *e){if(s->top<s->base){return ERROR;}*e = s->container[s->top];s->count--;}/*******************************************/main(){int dx[]={ 0, 1,1,1,0,-1,-1,-1}; /*方向偏移量*/int dy[]={-1,-1,0,1,1, 1, 0,-1};int direction,flag, /*找到出口标志*/cx,cy, /*当前位置坐标*/nx,ny; /*根据方向下一点坐标*/SqQueue sqq;SqStack sqs,sqsout;ElemType start,et;CreateRandomMaze();/*创建随机地图*/printMaze();/*打印地图*/InitQueue(&sqq); /*初始化队列sqq*/InitStack(&sqs); /*初始化栈sqs*/InitStack(&sqsout); /*初始化栈sqsout*/start.x=1;start.y=1;start.parentx=-1;start.parenty=-1;EnQueue(&sqq,start); /*起始点入队*/for(;(flag!=TRUE)&&(!QueueEmpty(sqq));){GetHead(sqq,&et); /*取队首元素(不是出队)*/cx=et.x;cy=et.y;for(direction=0;direction<8;direction++) /*朝八个方向试探*/{nx = cx+dx[direction]; /*nx = cx + 方向偏移量*/ny = cy+dy[direction];if(maze[ny][nx]==0||maze[ny][nx]==OUT) /*如果下一点坐标可通,或者是出口*/ {et.y=ny;et.parentx = cx; /*下一点的路径上的前驱是当前点*/et.parenty = cy;EnQueue(&sqq,et); /*将下一点入队*/if(maze[ny][nx]!=OUT){maze[ny][nx]=STEPPED; /*访问过了*/}}if(maze[ny][nx]==OUT) /*如果下一点是出口*/{flag = TRUE; /*标志位置TRUE*/break; /*路径已找到,不用在试探了,退出循环*/}}DeQueue(&sqq,&et);/*出队*/Push(&sqs,et);/*所有出队的元素都将别压入栈sqs*/}if(QueueEmpty(sqq)) /*如果每条路线都试过都没有找到出口,则队列为空,说明已经失败了*/ {printf("failed!");}if(flag==TRUE) /*如果标记为是TRUE,那么说明已经找到路径了,广度优先所的路径即为最短路径*/ {while(!QueueEmpty(sqq)){DeQueue(&sqq,&et);Push(&sqs,et);}/******以下用到了两个栈,第一个栈sqs用来搜索最短路径并把其存入第二个栈,第二个栈sqsout用于顺序输出********/Pop(&sqs,&start);Push(&sqsout,start);while(!StackEmpty(sqs)){Pop(&sqs,&et);if(et.x==start.parentx && et.y==start.parenty){start=et;Push(&sqsout,et);}}while(!StackEmpty(sqsout)) {Pop(&sqsout,&et);printf("(%d,%d)\t",et.x,et.y); }}}。
迷宫问题算法
迷宫问题算法一、引言迷宫问题是一个经典的算法问题,对于寻找路径的算法有着广泛的应用。
迷宫是一个由通路和墙壁组成的结构,从起点出发,要找到通往终点的路径。
迷宫问题算法主要解决的是如何找到一条从起点到终点的最短路径。
二、DFS(深度优先搜索)算法深度优先搜索算法是迷宫问题求解中最常用的算法之一。
其基本思想是从起点开始,沿着一个方向不断向前走,当走到无法继续前进的位置时,回退到上一个位置,选择另一个方向继续前进,直到找到终点或者无路可走为止。
1. 算法步骤1.初始化一个空栈,并将起点入栈。
2.当栈不为空时,取出栈顶元素作为当前位置。
3.如果当前位置是终点,则返回找到的路径。
4.如果当前位置是墙壁或者已经访问过的位置,则回退到上一个位置。
5.如果当前位置是通路且未访问过,则将其加入路径中,并将其邻居位置入栈。
6.重复步骤2-5,直到找到终点或者栈为空。
2. 算法实现伪代码以下为DFS算法的实现伪代码:procedure DFS(maze, start, end):stack := empty stackpath := empty listvisited := empty setstack.push(start)while stack is not empty docurrent := stack.pop()if current == end thenreturn pathif current is wall or visited.contains(current) thencontinuepath.append(current)visited.add(current)for each neighbor in getNeighbors(current) dostack.push(neighbor)return "No path found"三、BFS(广度优先搜索)算法广度优先搜索算法也是解决迷宫问题的常用算法之一。
y迷宫计算公式
y迷宫计算公式迷宫计算公式是指用于求解迷宫路径的数学模型或算法。
迷宫是由通道和阻塞区域构成的一种图形结构,求解迷宫路径即是要找到从起点到终点的通行路径。
迷宫计算公式有很多种,下面是其中几种常见的算法。
1. 深度优先搜索算法(DFS):深度优先搜索算法是一种经典的求解迷宫路径的算法。
它通过递归的方式深入搜索迷宫中的每一个可能的路径,直到找到终点或者无法继续深入为止。
算法步骤:(1)选择起点,并将其标记为已访问。
(2)按照上、右、下、左的顺序依次尝试访问相邻的格子,如果格子是通道且未访问过,则继续递归地进行搜索。
(3)如果找到终点,则输出路径;否则,回退到上一步。
(4)重复上述步骤,直到找到终点或者无法继续搜索。
2. 广度优先搜索算法(BFS):广度优先搜索算法是一种另外一种常用的求解迷宫路径的算法。
它是通过逐层地扩展搜索范围来寻找终点的方法。
算法步骤:(1)选择起点,并将其标记为已访问。
(2)将起点加入队列。
(3)重复以下步骤直到找到终点或者队列为空:- 从队列中取出一个格子;- 按照上、右、下、左的顺序依次尝试访问相邻的格子;- 如果格子是通道且未访问过,则将其标记为已访问,并将其加入队列。
(4)如果找到终点,则输出路径;否则,说明没有可行的路径。
3. A*算法:A*算法是一种启发式搜索算法,它使用一个估计函数来评估每个格子的优先级,从而选择下一个扩展的格子。
算法步骤:(1)初始化起点,并将其加入开放列表(open list)。
(2)重复以下步骤直到找到终点或者开放列表为空:- 从开放列表中选择优先级最高的格子,并将其从开放列表中移除。
- 如果选择的格子是终点,则输出路径。
- 否则,对其所有相邻的可通行格子进行以下操作:* 如果格子不在开放列表中,则将其加入开放列表,并计算该格子的估计值和移动代价。
* 如果格子已经在开放列表中,并且新的移动路径更短,则更新该格子的估计值和移动代价。
(3)如果开放列表为空,说明没有可行的路径。
数据结构课程设计报告-迷宫算法
沈阳航空航天大学课程设计报告课程设计名称:数据结构课程设计课程设计题目:迷宫算法院(系):计算机学院专业:计算机科学与技术班级:学号:姓名:指导教师:目录1 课程设计介绍 (1)1.1课程设计内容 (1)1.2课程设计要求 (1)2 课程设计原理 (2)2.1课设题目粗略分析 (2)2.2原理图介绍 (3)2.2.1 功能模块图 (3)2.2.2 流程图分析 (4)3 数据结构分析 (8)3.1存储结构 (8)3.2算法描述 (8)4 调试与分析 (11)4.1调试过程 (11)4.2程序执行过程 (11)参考文献 (15)附录(关键部分程序清单) (16)1 课程设计介绍1.1 课程设计内容编写算法能够生成迷宫,并且求解迷宫路径(求解出任意一条到出口的路径即可):1.迷宫用上下左右四种走法;2.迷宫的大小和复杂程度可以由用户定义;3.入口出口也由用户自己选择。
1.2 课程设计要求1.不必演示求解过程,只需要输出迷宫求解的路径;2.参考相应资料完成课设。
2 课程设计原理2.1 课设题目粗略分析根据课设题目要求,拟将整体程序分为四大模块。
以下是四个模块的大体分析:1 建立迷宫:要建立迷宫首先就要建立存储结构,这里我用栈的方式建立的。
根据用户输入的迷宫的大小(我设置的最大值为25可以根据要求调解);2 设置迷宫:这里将0设置围墙,1是可以通过的路径,-1是不可以通过路径,外墙是以设计好的,内墙需要用户来设置,障碍的难度可由用户自行定义;3 寻找路径:寻找路径我设置了四个方向{0,1},{1,0},{0,-1},{-1,0}移动方向,依次为东南西北,首先向东走,若不成功则转换方向,成功则继续前进,将走过的路径进行标记,然后存入栈中;4 输出结果:输出的结果分为两种,一种是用户建立的迷宫主要是让用户检查是否符合要求,第二种输出的是寻找完后的路径,路径用1 2 3 4···来表示。
迷宫算法——精选推荐
迷宫算法迷宫算法之迷宫⽣成和迷宫寻路算法三种迷宫⽣成算法1. DFS(即深度优先)算法⽣成,分为递归和⾮递归⽅法2. ⼗字分割算法⽣成,分为递归和⾮递归⽅法3. 随机 Prim 算法⽣成,⼀种⾮递归⽅法两种迷宫寻路算法1. DFS 寻路,本⽂采⽤⾮递归实现2. A* 寻路,⼀种⾮递归⽅法⼀些说明1. 代码实现语⾔:C++2. 环境:Win10 + VS20193. 迷宫同⼀要求:长宽均为奇数 N,最外围⼀圈是墙,⼊⼝坐标(0, 1),出⼝坐标(N-1, N-2)4. 由 EasyX 制作的迷宫算法可视化程序:三种迷宫⽣成算法最外围是墙或⼊⼝,因此操作范围是:(1, 1) 到 (N-2, N-2)DFS 算法⽣成(⼀种挖墙算法)1. 初始时全是墙2. x,y 均在 1~N-2 中的奇数随机选取⼀点(均为奇数),将其挖开,并将该点⼊栈3. 四个⽅向随机选取⼀个⽅向,设当前挖开坐标为(x, y),若该⽅向上满⾜ (x + dx*2, y + dy*2) 是墙(dx 和 dy 代表⽅向,取值为 1 或 -1),则挖开 (x + dx, y + dy),并重设当前点为 (x + dx*2, y + dy*2),将当前点⼊栈4. 以 Cur 为当前点,重复操作步骤 25. 若 Cur 不能挖开周围的墙,则栈执⾏ pop 操作,并将 Cur 重置为此时栈中的 top 元素6. 直到栈为空,说明挖墙操作结束⽣成形态:源码(包含迭代和⾮迭代版,注释 EasyX 的地⽅是⽤ EasyX 绘图):#include <iostream>#include <ctime>#include <stack>#include <vector>#include <algorithm>#include <easyx.h>using namespace std;// 迷宫格⼦状态enum CellState:int { PATH = 0, WALL, FLAG };// 迷宫格⼆维点结构struct Point2{int x, y;Point2(int _x, int _y) :x(_x), y(_y) {}};// 迷宫⼤⼩(要求为奇数)const int mazeSize = 21;// 迷宫⽣成接⼝--递归版void DFS_generator(int _x, int _y, std::vector<std::vector<int>>& maze){// 定义⽅向容器std::vector<std::vector<int>> dir{ {1,0},{-1,0},{0,1},{0,-1} };// 随机打乱⽅向std::random_shuffle(dir.begin(), dir.end());// 递归⽣成迷宫maze[_x][_y] = PATH;for (int i = 0; i < 4; ++i){if (_x + 2 * dir[i][0] >= 1 && _x + 2 * dir[i][0] <= mazeSize - 2 && _y + 2 * dir[i][1] >= 1 && _y + 2 * dir[i][1] <= mazeSize - 2&& maze[_x + 2 * dir[i][0]][_y + 2 * dir[i][1]] == WALL){maze[_x + dir[i][0]][_y + dir[i][1]] = PATH;DFS_generator(_x + 2 * dir[i][0], _y + 2 * dir[i][1], maze);}}}// 迷宫⽣成接⼝--迭代版void DFS_iterative_generator(std::vector<std::vector<int>>& maze){// 定义栈容器std::stack<Point2> sp;// 定义⽅向容器std::vector<std::vector<int>> dir{ {1,0},{-1,0},{0,1},{0,-1} };// 要求参数为奇数Point2 temp((rand() % (mazeSize - 2) + 1) | 1, (rand() % (mazeSize - 2) + 1) | 1);sp.push(temp);// 后续迭代⽣成迷宫,并绘制while (!sp.empty()){if (maze[temp.x][temp.y] != PATH)maze[temp.x][temp.y] = PATH;// 随机打乱⽅向std::random_shuffle(dir.begin(), dir.end());int i = 0;for (; i < 4; ++i){if (temp.x + 2 * dir[i][0] >= 1 && temp.x + 2 * dir[i][0] <= mazeSize - 2 && temp.y + 2 * dir[i][1] >= 1 && temp.y + 2 * dir[i][1] <= mazeSize - 2 && maze[temp.x + 2 * dir[i][0]][temp.y + 2 * dir[i][1]] == WALL){maze[temp.x + dir[i][0]][temp.y + dir[i][1]] = PATH;temp.x += 2 * dir[i][0];temp.y += 2 * dir[i][1];sp.push(temp);break;}}if (i == 4) sp.pop();if (!sp.empty()) temp = sp.top();}}// main 函数int main(){srand((unsigned)time(nullptr));// ⼊⼝出⼝Point2 start(0, 1);Point2 end(mazeSize - 1, mazeSize - 2);// ⼆维迷宫容器std::vector<std::vector<int>> maze;// 初始化迷宫for (int i = 0; i < mazeSize; ++i) maze.push_back(std::vector<int>());for (int i = 0; i < mazeSize; ++i)for (int j = 0; j < mazeSize; ++j)maze[i].push_back(WALL);maze[start.x][start.y] = maze[end.x][end.y] = PATH;// ⽣成迷宫(迭代和⾮迭代⼆选⼀⽣成)DFS_generator((rand() % (mazeSize - 2) + 1) | 1, (rand() % (mazeSize - 2) + 1) | 1, maze);// DFS_iterative_generator(maze);// 打印迷宫for (int j = 0; j < mazeSize; ++j){for (int i = 0; i < mazeSize; ++i)cout << maze[i][j] << " ";cout << endl;}// EasyX{auto ret = _getwch();const int width = 15;initgraph(mazeSize * width, mazeSize * width);setlinecolor(DARKGRAY);setfillcolor(LIGHTGRAY);for (int j = 0; j < mazeSize; ++j)for (int i = 0; i < mazeSize; ++i)if (maze[i][j] == WALL)fillrectangle(i * width, j * width, i * width + width - 1, j * width + width - 1);// saveimage(_T("D:\\maze.png"));ret = _getwch();closegraph();}return 0;}⼗字分割算法⽣成(是⼀种⼗字补墙算法)1. 初始时除了四周全通路2. x,y 均在 1~N-2 中随机选取⼀点(均为偶数),然后⼗字建墙3. 在建好的四⾯墙(不包含选取点)中随机选择三⾯,找奇数点开洞,使得四个⼦空间连通4. 对四个⼦空间重复操作,直到⼦空间不可分割为⽌(淡黄⾊是开洞位置)(何时不可分割?长度或宽度不⼤于 1 时)⽣成形态:源码(包含迭代和⾮迭代版,注释 EasyX 的地⽅是⽤ EasyX 绘图):#include <iostream>#include <ctime>#include <stack>#include <vector>#include <algorithm>#include <easyx.h>using namespace std;// 迷宫格⼦状态enum CellState:int { PATH = 0, WALL, FLAG };// 迷宫格⼆维点结构struct Point2{int x, y;Point2(int _x, int _y) :x(_x), y(_y) {}};// 四维点,⽤于分割矩形区间struct Point4{int x1, x2;int y1, y2;Point4(int _x1, int _x2, int _y1, int _y2) :x1(_x1), x2(_x2), y1(_y1), y2(_y2) {}};// 迷宫⼤⼩(要求为奇数)const int mazeSize = 21;// 迷宫⽣成接⼝--递归版void Division_generator(int _l, int _r, int _t, int _b, std::vector<std::vector<int>>& maze){// 间隔⼤于 1 时可分割if (_r - _l > 1 && _b - _t > 1){int i = 0;// 要求分割点 px,py 为偶数int px = ((rand() % (_r - _l) + _l + 1) | 1) - 1;int py = ((rand() % (_b - _t) + _t + 1) | 1) - 1;while (px + i <= _r || px - i >= _l || py + i <= _b || py - i >= _t){if (px + i <= _r) maze[px + i][py] = WALL;if (px - i >= _l) maze[px - i][py] = WALL;if (py + i <= _b) maze[px][py + i] = WALL;if (py - i >= _t) maze[px][py - i] = WALL;++i;}// 定义⽅向容器,随机在三⾯墙上开洞// 要求开洞位置是奇数std::vector<int> dir{ 0,1,2,3 };std::random_shuffle(dir.begin(), dir.end());for (int i = 0; i < 3; ++i){if (dir[i] == 0){int xx = (rand() % (px - _l) + _l) | 1;maze[xx][py] = PATH;}else if (dir[i] == 1){int xx = (rand() % (_r - px) + px) | 1;maze[xx][py] = PATH;}else if (dir[i] == 2){int yy = (rand() % (py - _t) + _t) | 1;maze[px][yy] = PATH;}else if (dir[i] == 3){int yy = (rand() % (_b - py) + py) | 1;maze[px][yy] = PATH;}}// 递归分割Division_generator(_l, px - 1, _t, py - 1, maze);Division_generator(px + 1, _r, _t, py - 1, maze);Division_generator(_l, px - 1, py + 1, _b, maze);Division_generator(px + 1, _r, py + 1, _b, maze);}}// 迷宫⽣成接⼝--迭代版void Division_iterative_generator(std::vector<std::vector<int>>& maze){// 定义栈容器std::stack<Point4> sp;// 定义⽅向容器std::vector<int> dir{ 0,1,2,3 };// 要求参数为奇数Point4 temp(1, mazeSize - 2, 1, mazeSize - 2);sp.push(temp);// 后续迭代⽣成迷宫while (!sp.empty()){sp.pop();if (temp.x2 - temp.x1 > 1 && temp.y2 - temp.y1 > 1){int i = 0;int px = ((rand() % (temp.x2 - temp.x1) + temp.x1 + 1) | 1) - 1;int py = ((rand() % (temp.y2 - temp.y1) + temp.y1 + 1) | 1) - 1;while (px + i <= temp.x2 || px - i >= temp.x1 || py + i <= temp.y2 || py - i >= temp.y1) {if (px + i <= temp.x2) maze[px + i][py] = WALL;if (px - i >= temp.x1) maze[px - i][py] = WALL;if (py + i <= temp.y2) maze[px][py + i] = WALL;if (py - i >= temp.y1) maze[px][py - i] = WALL;++i;}// 随机在三⾯墙上开洞,要求开洞位置是奇数std::random_shuffle(dir.begin(), dir.end());for (int i = 0; i < 3; ++i){if (dir[i] == 0){int xx = (rand() % (px - temp.x1) + temp.x1) | 1;maze[xx][py] = PATH;}else if (dir[i] == 1){int xx = (rand() % (temp.x2 - px) + px) | 1;maze[xx][py] = PATH;}else if (dir[i] == 2){int yy = (rand() % (py - temp.y1) + temp.y1) | 1;maze[px][yy] = PATH;}else if (dir[i] == 3){int yy = (rand() % (temp.y2 - py) + py) | 1;maze[px][yy] = PATH;}}// 将三个⽅块区间⼊栈sp.push(Point4(px + 1, temp.x2, py + 1, temp.y2));sp.push(Point4(temp.x1, px - 1, py + 1, temp.y2));sp.push(Point4(px + 1, temp.x2, temp.y1, py - 1));temp.x2 = px - 1;temp.y2 = py - 1;sp.push(temp);}else if (!sp.empty()) { temp = sp.top(); }}}// main 函数int main(){srand((unsigned)time(nullptr));// ⼊⼝出⼝Point2 start(0, 1);Point2 end(mazeSize - 1, mazeSize - 2);// ⼆维迷宫容器std::vector<std::vector<int>> maze;// 初始化迷宫for (int i = 0; i < mazeSize; ++i) maze.push_back(std::vector<int>());for (int i = 0; i < mazeSize; ++i)for (int j = 0; j < mazeSize; ++j)(i == 0 || j == 0 || i == mazeSize - 1 || j == mazeSize - 1) ? maze[i].push_back(WALL) : maze[i].push_back(PATH); maze[start.x][start.y] = maze[end.x][end.y] = PATH;// ⽣成迷宫(迭代和⾮迭代⼆选⼀⽣成)Division_generator(1, mazeSize - 2, 1, mazeSize - 2, maze);// Division_iterative_generator(maze);// 打印迷宫for (int j = 0; j < mazeSize; ++j){for (int i = 0; i < mazeSize; ++i)cout << maze[i][j] << " ";cout << endl;}// EasyX{auto ret = _getwch();const int width = 15;initgraph(mazeSize * width, mazeSize * width);setlinecolor(DARKGRAY);setfillcolor(LIGHTGRAY);for (int j = 0; j < mazeSize; ++j)for (int i = 0; i < mazeSize; ++i)if (maze[i][j] == WALL)fillrectangle(i * width, j * width, i * width + width - 1, j * width + width - 1);// saveimage(_T("D:\\maze.png"));ret = _getwch();closegraph();}return 0;}随机 Prim 算法⽣成(⼀种⾮递归⽅法)1. 初始时全是墙2. 构建⼀墙⼀通路形式3. 随机选择⼀个通路,并将周围墙⼊容器,标记该通路4. 在墙容器中随机选取⼀堵墙,如果墙两边的通路没有同时被标记,则打通该墙,并将原来未被标记的通路周围的墙加⼊容器,然后将该通路标记,最后移除该墙5. 重复操作 4,直到墙容器为空,说明该算法完成,最后将被标记的通路清除标记⽣成形态:源码(注释 EasyX 的地⽅是⽤ EasyX 绘图):#include <iostream>#include <ctime>#include <stack>#include <vector>#include <algorithm>#include <easyx.h>using namespace std;// 迷宫格⼦状态enum CellState:int { PATH = 0, WALL, FLAG };// 迷宫格⼆维点结构struct Point2{int x, y;Point2(int _x, int _y) :x(_x), y(_y) {}};// 迷宫⼤⼩(要求为奇数)const int mazeSize = 21;// 迷宫⽣成接⼝void Prim_generator(std::vector<std::vector<int>>& maze){// 构建墙隔开通路的迷宫,奇数点为通路for (int i = 1; i <= mazeSize - 2; i += 2)for (int j = 1; j <= mazeSize - 2; j += 2)maze[i][j] = PATH;// 维护⼀个墙容器std::vector<Point2> vp;// 先随机找⼀个通路Point2 temp((rand() % (mazeSize - 2) + 1) | 1, (rand() % (mazeSize - 2) + 1) | 1);// 将周围墙⼊栈if (temp.x - 1 >= 2) vp.push_back(Point2(temp.x - 1, temp.y));if (temp.x + 1 <= mazeSize - 3) vp.push_back(Point2(temp.x + 1, temp.y));if (temp.y - 1 >= 2) vp.push_back(Point2(temp.x, temp.y - 1));if (temp.y + 1 <= mazeSize - 3) vp.push_back(Point2(temp.x, temp.y + 1));// 标记该通路maze[temp.x][temp.y] = FLAG;int pos = 0;// 后续迭代⽣成迷宫while (!vp.empty()){// 在墙容器中随机选取⼀堵墙pos = rand() % vp.size();temp = vp[pos];// 记录该墙是否打通bool flag = false;// 后续 if else 判断墙所隔离通路在左右还是上下,并判断是否打通if (maze[temp.x + 1][temp.y] == WALL){if (maze[temp.x][temp.y - 1] != maze[temp.x][temp.y + 1]){maze[temp.x][temp.y] = PATH;// 对新加⼊的通路进⾏标记if (maze[temp.x][temp.y - 1] == FLAG) { maze[temp.x][temp.y + 1] = FLAG; ++temp.y; } else { maze[temp.x][temp.y - 1] = FLAG; --temp.y; }flag = true;}}else{if (maze[temp.x - 1][temp.y] != maze[temp.x + 1][temp.y]){maze[temp.x][temp.y] = PATH;// 对新加⼊的通路进⾏标记if (maze[temp.x - 1][temp.y] == FLAG) { maze[temp.x + 1][temp.y] = FLAG; ++temp.x; }else { maze[temp.x - 1][temp.y] = FLAG; --temp.x; }flag = true;}}// 如果打通了墙,将进加⼊的通路周围的墙⼊容器if (flag){if (temp.x - 1 >= 2 && maze[temp.x - 1][temp.y] == WALL) vp.push_back(Point2(temp.x - 1, temp.y));if (temp.x + 1 <= mazeSize - 3 && maze[temp.x + 1][temp.y] == WALL) vp.push_back(Point2(temp.x + 1, temp.y)); if (temp.y - 1 >= 2 && maze[temp.x][temp.y - 1] == WALL) vp.push_back(Point2(temp.x, temp.y - 1));if (temp.y + 1 <= mazeSize - 3 && maze[temp.x][temp.y + 1] == WALL) vp.push_back(Point2(temp.x, temp.y + 1)); }// 移除该墙vp[pos] = *(vp.end() - 1);vp.pop_back();}// 将被标记的通路还原for (auto& v1 : maze)for (auto& v2 : v1)if (v2 == FLAG) v2 = PATH;}// main 函数int main(){srand((unsigned)time(nullptr));// ⼊⼝出⼝Point2 start(0, 1);Point2 end(mazeSize - 1, mazeSize - 2);// ⼆维迷宫容器std::vector<std::vector<int>> maze;// 初始化迷宫for (int i = 0; i < mazeSize; ++i) maze.push_back(std::vector<int>());for (int i = 0; i < mazeSize; ++i)for (int j = 0; j < mazeSize; ++j)maze[i].push_back(WALL);maze[start.x][start.y] = maze[end.x][end.y] = PATH;// ⽣成迷宫Prim_generator(maze);// 打印迷宫for (int j = 0; j < mazeSize; ++j){for (int i = 0; i < mazeSize; ++i)cout << maze[i][j] << " ";cout << endl;}// EasyX{auto ret = _getwch();const int width = 15;initgraph(mazeSize * width, mazeSize * width);setlinecolor(DARKGRAY);setfillcolor(LIGHTGRAY);for (int j = 0; j < mazeSize; ++j)for (int i = 0; i < mazeSize; ++i)if (maze[i][j] == WALL)fillrectangle(i * width, j * width, i * width + width - 1, j * width + width - 1);// saveimage(_T("D:\\maze.png"));ret = _getwch();closegraph();}return 0;}两种迷宫寻路算法DFS 寻路,采⽤⾮递归实现该寻路⽅法,应该算是⽐较简单算法,学习数据结构栈时⼀般就会接触该算法从起点开始,将当前点⼊栈,向某⼀⽅向前进。
迷宫问题算法
迷宫问题算法随着计算机技术的发展,我们能够利用计算机的能力来解决一些复杂的问题。
其中一个有意思的问题就是迷宫问题,也就是如何从一个迷宫的入口走到出口。
本文将向大家介绍迷宫问题的算法及其实现。
一、迷宫问题的形式化定义一个迷宫可以被看做是一个有向图,其中每个节点表示一个房间,边表示房间之间的通路。
我们假设每个房间有四个方向,上下左右,故有向图的每个节点最多有四个邻居节点。
假设起点为S,终点为T,每个节点的代价为1,表示每个走过的房间代价都是一样的。
我们的目标是找到一条S到T的最短路径。
如果这条路径不存在,则说明从S无法到达T。
二、基于深度优先搜索的解法深度优先搜索是一种基于回溯的搜索方法,其思路是从起点开始,递归地遍历每个节点,在遍历过程中标记已访问过的节点,直到找到终点或者所有节点都被遍历过。
对于迷宫问题,深度优先搜索的具体实现可以作为如下所示:```pythondef dfs(maze, visited, x, y, endX, endY, steps):if x == endX and y == endY:return stepsif visited[x][y]:return float('inf')visited[x][y] = TrueminSteps = float('inf')for dx, dy in ((0, 1), (1, 0), (0, -1), (-1, 0)):nx, ny = x + dx, y + dyif 0 <= nx < len(maze) and 0 <= ny < len(maze[0]) and maze[nx][ny] == 0:newSteps = dfs(maze, visited, nx, ny, endX, endY, steps + 1)minSteps = min(minSteps, newSteps)visited[x][y] = Falsereturn minSteps```在这个实现中,我们使用了一个visited数组来记录每个节点是否被访问过,1表示被访问过,0表示未被访问过。
数据结构实验报告迷宫
数据结构实验报告迷宫数据结构实验报告:迷宫引言:迷宫是一种融合了游戏与智力的有趣结构,它可以激发人们的思考能力和解决问题的能力。
在本次数据结构实验中,我们将探索迷宫的构建和求解方法,通过编程实现一个迷宫的生成和解决算法。
一、迷宫的生成算法1.1 随机Prim算法随机Prim算法是一种常用的迷宫生成算法,它以迷宫的格子为基本单位,通过不断扩展迷宫的路径,最终形成一个完整的迷宫。
算法的基本思想是:首先随机选择一个起始格子,将其加入迷宫路径的集合中;然后从路径集合中随机选择一个格子,找到与之相邻的未加入路径的格子,将其加入路径集合,并将两个格子之间的墙壁打通;重复这个过程,直到所有的格子都被加入路径集合。
1.2 递归分割算法递归分割算法是另一种常用的迷宫生成算法,它以迷宫的墙壁为基本单位,通过不断分割墙壁,最终形成一个完整的迷宫。
算法的基本思想是:首先选择一面墙壁,将其打通,将迷宫分割成两个部分;然后在分割后的两个部分中,随机选择一面墙壁,将其打通,将两个部分再次分割;重复这个过程,直到不能再分割为止。
二、迷宫的求解算法2.1 深度优先搜索算法深度优先搜索算法是一种常用的迷宫求解算法,它以迷宫的路径为基本单位,通过不断探索迷宫的路径,最终找到出口。
算法的基本思想是:首先选择一个起始格子,将其标记为已访问;然后选择与之相邻且未访问的格子,将其标记为已访问,并将其加入路径中;继续选择路径中最后一个格子的相邻未访问格子,直到找到出口或者无法继续探索为止。
2.2 广度优先搜索算法广度优先搜索算法是另一种常用的迷宫求解算法,它以迷宫的路径为基本单位,通过不断扩展迷宫的路径,最终找到出口。
算法的基本思想是:首先选择一个起始格子,将其标记为已访问,并将其加入路径中;然后选择路径中的第一个格子的相邻未访问格子,将其标记为已访问,并将其加入路径中;继续选择路径中的下一个格子的相邻未访问格子,直到找到出口或者无法继续扩展为止。
迷宫算法
else{ if(!StackEmpty(S)){ Pop(S,e); while(e.di==4 && !StackEmpty(S)) { MarkPrint(e.seat);Pop(S,e); }//while if(e.di<4) { e.di++; Push(S,e); curpos=NextPos(e.seat,e.di); }//if }//if }//else }while(!StackEmpty(S)); return (false); }
Status MazePath(MazeType &maze,PosType start,PosType end) { Stack S; PosType curpos; int curstep; ElemType e; InitStack(S); curpos=start; curstep=1; do { if(Pass(curpos)){ FootPrint(curpos); e=(curstep,curpos,1); Push(S,e); if(curpos==end) return(true); curpos=NextPos(curpos,1); curstep++; }
例四、 例四、 迷宫求解
通常用的是“穷举求解”的方法 “穷举求解” # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
c语言随机生成迷宫算法
c语言随机生成迷宫算法
c语言随机生成迷宫算法是一种常用的算法,用于生成迷宫结构。
该算法的核心思想是通过随机生成迷宫中的墙壁和通路,来创建一个具有迷宫结构的随机图。
具体实现过程可以分为以下几个步骤:
1.初始化迷宫矩阵:创建一个二维数组,用于表示迷宫的结构。
将数组中所有元素初始化为墙壁。
2.随机选取起点:从迷宫中随机选取一个起点。
3.随机生成迷宫结构:从当前位置开始,随机选择一个方向并移动,如果该方向上的位置未被访问过,则将当前位置与该位置之间的墙壁打通,并将该位置标记为已访问。
如果该方向上的位置已被访问,则不进行操作,直接选择下一个方向。
4.生成终点:在生成迷宫结构的过程中,可以随机选择某些位置作为终点。
当达到终点时,迷宫生成结束。
5.输出迷宫结构:将迷宫矩阵输出到控制台或文件中,以便用户查看。
c语言随机生成迷宫算法可以用于游戏开发、路径规划等领域。
其优点是生成的迷宫结构随机性高,具有挑战性,而且可以根据需求进行调整。
缺点是算法复杂度较高,需要耗费大量时间和计算资源。
- 1 -。
迷宫问题的算法
迷宫问题的算法(优于递归、深度优先、广度优先)在一个n*m的迷宫里,每一个坐标点有两种可能:0或1,0表示该位置允许通过,1表示该位置不允许通过.如地图:0 0 0 0 01 0 1 0 10 0 1 1 10 1 0 0 00 0 0 1 0最短路径应该是AB0001C101ED111F1JKLGHI1M即:(1,1)-(1,2)-(2,2)-(3,2)-(3,1)-(4,1)-(5,1)-(5,2)-(5,3)-(4,3)-(4,4)-(4,5)-(5,5) 由input.txt中输入一个迷宫,以坐标的方式输出从左上角到右下角的最短路径.如:input(from input.txt):5 50 0 0 0 01 0 1 0 10 0 1 1 10 1 0 0 00 0 0 1 0output (to screen):(1,1)-(1,2)-(2,2)-(3,2)-(3,1)-(4,1)-(5,1)-(5,2)-(5,3)-(4,3)-(4,4)-(4,5)-(5,5)input:5 50 0 0 0 01 1 1 0 10 0 1 1 10 1 0 0 00 0 0 1 0output:There is no way to leave!算法分析:如示例地图:0000010101001110100000010我们可知,点[5,5]到点[5,5]的距离为0(废话)因此,我们把该位置标志为0,点[4,5]到点[5,5]的距离为点[5,5]到点[5,5]的距离+1,也即1.点[4,4]到点[4,5]的距离至多为点[4,5]到点[5,5]的距离+1,因为点[4,4]可以直接到达点[4,5],所以点[4,4]到点[5,5]的距离为2;....点[1,1]到点[5,5]的距离为12,并且可以证明是最短距离.此时,由于点[1,1]到点[5,5]的距离为12,且点[1,2]到点[5,5]的距离为11,我们便可以知道,从点[1,1]到点[1,2],一定是更接近点[5,5]而不是更远离,接下去我们找点[1,2]周围距离为10的,[1,3]周围距离为9的......直到找到[5,5],我们找到的就是最短路径.算法用C语言表示大致如下:main(){int a[100][100],b[100][100];int n,m,i,j,x,y;int bo; file://标志每一次操作对数组B是否有改动,如果没有改动说明搜索完毕.file://读入N,M,A;file://将B[N,M]记为0;将bo记为1;while (bo==1){bo=0;for (i=1;i<=n;i++)for (j=1;j<=n;j++)if ((b[i][j]>-1)){file://搜寻[i,j]的上下左右四个方向,检查是否存在仍为-1或者大于b[i][j]+1的项,if (存在){b[i+dx][j+dy]=b[i][j]+1;//dx,dy∈{-1,0,1}且dx*dy=0bo=1;}}}if (b[1][1]==-1) {//表示没有结果,退出程序.}j=b[1][1];x=1;y=1;//x和y表示当前的坐标.printf("(1,1)");for (i=1;i<=j;i++){搜索[x,y]周围[x+dx][y+dy]使得p[x+dx][y+dy]=p[x][y]-1;x=x+dx;y=y+dy;printf("-(%d,%d)",x,y);}}以下是我的程序,在TC++3.0下运行通过,在VC下运行需要修改程序,并可适量增加数组大小。
迷宫生成原理
迷宫生成原理迷宫生成是一个经典的计算机算法问题,它主要涉及到图的生成与遍历的基本原理。
在这篇文章里,我们将会介绍迷宫生成的几种常用的算法和原理。
一.迷宫的定义和表示迷宫是一个由墙壁和路径组成的结构,其中墙壁是不可穿越的,而路径可以用来连接起不同的地点。
迷宫可以看作是一个有向图,每个节点表示迷宫中的一个位置,边则表示所连接的路径。
迷宫一般是在一个二维平面上进行。
为了方便起见,我们可以用一个二维矩阵来表示迷宫。
矩阵的每一个元素可以是一个布尔值,其中True表示墙壁,False表示路径。
迷宫的入口通常位于矩阵的某个边界上,出口也位于另一个边界上。
二.深度优先搜索算法(DFS)深度优先搜索算法是生成迷宫的一种常用方法。
它基于图的深度优先遍历的原理,在遍历过程中生成路径并打破墙壁。
1.初始化迷宫矩阵和堆栈数据结构。
迷宫矩阵全部设置为墙壁,堆栈中只包含迷宫的入口节点。
2.从堆栈中取出一个节点,将其标记为路径,并将其未访问的邻居节点入栈。
3.重复步骤2,直到堆栈为空。
在遍历的过程中打破一些墙壁,以生成路径。
4.最后,迷宫的矩阵中False表示路径,True表示墙壁。
三.随机化Prim算法Prim算法是另一种常用的迷宫生成算法,它是基于图的最小生成树的原理进行构建。
1.初始化迷宫矩阵和两个集合(集合A和B),其中A为已访问的节点集合,B为未访问的节点集合。
2.随机选择一个节点作为起始节点,并将其放入集合A中。
将其未访问的邻居节点加入集合B中。
3.随机选择一个在集合A中的节点a,并随机选择一个在集合B中的节点b。
将b的墙壁打破,并将b加入集合A中,将其未访问的邻居节点加入集合B中。
4.重复步骤3,直到集合B为空。
5.最后,迷宫的矩阵中False表示路径,True表示墙壁。
四.随机化Kruskal算法Kruskal算法也是一种常用的迷宫生成算法,它是基于图的最小生成树的原理进行构建。
1.随机选择一个节点,并将其作为一个独立的集合。
迷宫的深度广度算法
迷宫的深度⼴度算法解决杭电1010题⽬的意思就是求从开始点到终点的经过的边的个数和(即经过的总的点数减去⼀)等于给定的T对于迷宫问题,由于所求的路径不⼀定是最短的,所以不适合⽤⼴度优先遍历。
基础知识奇偶剪枝:t表⽰⾮最短路径⾛的步数,开始点为(sx,sy),结束点位(ex,ey) 那么从开始点到结束点的最短路径为 abs(ex-sx)+abs(ey-sy) ,那么t-(abs(ex-sx)+abs(ey-sy))为⾮奇数,因为步数是成对出现上下,左右。
还有⼀个修剪知识假设 N⾏,M列矩阵中有walls个墙,其中t表⽰要求的路径长度,那么当N*M-walls<=t时,不存在通路。
证明当存在从开始点到终点的通路时,N*M=walls+1+1+value,其中value表⽰除开始和结束点外的所有可⽤点。
⽽value+1+1-1>=t,从⽽value+1>=t从⽽ N*M-walls>=1+t ,所以当 N*M-wallls<=t时,不存在通路。
⼴度优先遍历算法如图:package plete;import java.util.Scanner;//深度优先遍历,解决问题public class pro1010MN {public static char Maze[][];public static PointNMs move[] = { new PointNMs(1, 0), new PointNMs(0, -1),new PointNMs(-1, 0), new PointNMs(0, 1) };// 右,下,左,上public static PointNMs endPoint;public static PointNMs startPoint;// ⽤于剪枝 m*n-wall<=t表⽰不存在满⾜条件的情况public static int walls;// 表⽰墙的个数public static boolean sum_flage;public static int sum_seecond;public static int row;public static int column;public static void main(String[] args) {Scanner scanner = new Scanner(System.in);row = scanner.nextInt();column = scanner.nextInt();sum_seecond = scanner.nextInt();scanner.nextLine();// 吃掉换⾏符while (row != 0 && column != 0 && sum_seecond != 0) {InitData(row, column);for (int i = 1; i < row + 1; i++) {String rowdatas = scanner.nextLine();SetInputData(i, rowdatas);}boolean flage = true;if (row * column - walls <= sum_seecond)// 剪枝操作flage = false;if (flage) {Maze[startPoint.x][startPoint.y] = 'X';MazeDepPath(startPoint.x, startPoint.y, 0);}if (flage && sum_flage)System.out.println("YES");elseSystem.out.println("NO");row = scanner.nextInt();column = scanner.nextInt();sum_seecond = scanner.nextInt();scanner.nextLine();}}private static void MazeDepPath(int x, int y, int time) {if (x <= 0 || x > row || y <= 0 || y > column)//判断边界,少加这⼀条语句,会出现错误return;if (x == endPoint.x && y == endPoint.y && time == sum_seecond) {sum_flage = true;return;}if (sum_flage)return;int tempt = (sum_seecond - time)- (Math.abs(x - endPoint.x) + Math.abs(y - endPoint.y));if (tempt < 0 || tempt % 2 == 1)//剪枝操作return;for (int i = 0; i < move.length; i++) {int xx = x + move[i].x;int yy = y + move[i].y;if (Maze[xx][yy] != 'X') {Maze[xx][yy] = 'X';MazeDepPath(xx, yy, time + 1);Maze[xx][yy] = '.';if (sum_flage)return;}}}private static void InitData(int row, int column) {Maze = new char[row + 2][];walls = 0;sum_flage = false;for (int i = 0; i < row + 2; i++) {Maze[i] = new char[column + 2];// 制作⼀堵墙Maze[i][column + 1] = Maze[i][0] = 'X';}// 制作⼀堵墙for (int j = 0; j < column + 2; j++) {Maze[0][j] = Maze[row + 1][j] = 'X';}}private static void SetInputData(int i, String rowdatas) {// char字符数组char[] charArray = rowdatas.trim().toCharArray();for (int j = 0; j < charArray.length; j++) {Maze[i][j + 1] = charArray[j];if (charArray[j] == 'D')endPoint = new PointNMs(i, j + 1);if (charArray[j] == 'S')startPoint = new PointNMs(i, j + 1);if (charArray[j] == 'X')walls++;}}}class PointNMs {int x;int y;public PointNMs(int x, int y) {this.x = x;this.y = y;}public boolean isEqual(PointNMs a) {return (a.x == this.x && a.y == this.y) ? true : false;}}迷宫的⼴度优先遍历算法如下:找出起始点到终点的最短路径package plete;import java.util.ArrayDeque;import java.util.Scanner;import java.util.Stack;/*** 4 4 5S.....X...XD....Yes(1,1)(1,2)(2,2)(3,2)(4,2)(4,3)(4,4)(3,4)*///不能⽤⼴度优先遍历处理,因为路径不⼀定是最短的,但此遍历⽅法求出的是最短路径public class pro1010M {public static char Maze[][];public static PointNM pre[][];public static ArrayDeque<PointNM> qpoints;public static PointNM move[] = { new PointNM(1, 0), new PointNM(0, -1), new PointNM(-1, 0), new PointNM(0, 1) };// 右,下,左,上public static PointNM endPoint;public static PointNM startPoint;public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int row = scanner.nextInt();int column = scanner.nextInt();int sum_seecond = scanner.nextInt();scanner.nextLine();// 吃掉换⾏符while (row != 0 && column != 0 && sum_seecond != 0) {InitData(row, column);for (int i = 1; i < row + 1; i++) {String rowdatas = scanner.nextLine();SetInputData(i, rowdatas);}if (MazePath(row, column, endPoint, startPoint)&& PrintPath(endPoint, startPoint, sum_seecond))System.out.println("YES");elseSystem.out.println("NO");row = scanner.nextInt();column = scanner.nextInt();sum_seecond = scanner.nextInt();scanner.nextLine();}}// 获取路径坐标序列private static boolean PrintPath(PointNM endPoint, PointNM startPoint, int sum_seecond) {Stack<PointNM> st = new Stack<PointNM>();while (!endPoint.isEqual(startPoint)) {st.push(endPoint);endPoint = pre[endPoint.x][endPoint.y];}st.push(startPoint);/** while (!st.isEmpty()) { PointNM pop = st.pop();* System.out.println("(" + pop.x + "," + pop.y + ") "); }*/return (sum_seecond - st.size() + 1) == 0 ? true : false;}// 获得数据路径,是否可⾏private static boolean MazePath(int row, int column, PointNM endPoint,PointNM startPoint) {if (endPoint.isEqual(startPoint))return true;qpoints.offer(startPoint);Maze[startPoint.x][startPoint.y] = 'X';while (!qpoints.isEmpty()) {PointNM nowPoint = qpoints.poll();for (int i = 0; i < move.length; i++) {// 每个⽅向⼴度遍历if (nowPoint.x + move[i].x == endPoint.x&& nowPoint.y + move[i].y == endPoint.y) {pre[endPoint.x][endPoint.y] = new PointNM(nowPoint.x,nowPoint.y);Maze[endPoint.x][endPoint.y] = 'X';return true;}if (Maze[nowPoint.x + move[i].x][nowPoint.y + move[i].y] == '.') {int x = nowPoint.x + move[i].x;int y = nowPoint.y + move[i].y;PointNM tempt = new PointNM(x, y);pre[tempt.x][tempt.y] = new PointNM(nowPoint.x, nowPoint.y);qpoints.offer(tempt);Maze[x][y] = 'X';}}}return false;}private static void SetInputData(int i, String rowdatas) {// char字符数组char[] charArray = rowdatas.trim().toCharArray();for (int j = 0; j < charArray.length; j++) {Maze[i][j + 1] = charArray[j];if (charArray[j] == 'D')endPoint = new PointNM(i, j + 1);if (charArray[j] == 'S')startPoint = new PointNM(i, j + 1);}}private static void InitData(int row, int column) {Maze = new char[row + 2][];pre = new PointNM[row + 2][];qpoints = new ArrayDeque<PointNM>();for (int i = 0; i < row + 2; i++) {Maze[i] = new char[column + 2];pre[i] = new PointNM[column + 2];// 制作⼀堵墙Maze[i][column + 1] = Maze[i][0] = 'X';}// 制作⼀堵墙for (int j = 0; j < column + 2; j++) {Maze[0][j] = Maze[row + 1][j] = 'X';}}}class PointNM {int x;int y;public PointNM(int x, int y) {this.x = x;this.y = y;}public boolean isEqual(PointNM a) {return (a.x == this.x && a.y == this.y) ? true : false; }}。
迷宫算法详解
迷宫算法详解
嘿,朋友们!今天咱就来好好唠唠迷宫算法!
你想啊,迷宫就像是一个错综复杂的大谜团,对吧?咱走在迷宫里,不就像一只无头苍蝇到处乱撞嘛(就如同你在一个陌生的城市里找路一样)!而迷宫算法呢,就是帮助我们找到走出这个谜团的路。
比如说深度优先搜索算法。
这就好像你下定决心要一条道走到黑,不撞南墙不回头(就像你非要搞清楚一个谜题,不管多困难都要试下去)!你沿着一个方向一直走,直到走不通了,再退回来换个方向走。
还有广度优先搜索算法呢。
它就像是你小心翼翼地一层一层探索,把每一个可能的方向都先探一探(就如同你在整理房间,先把这个区域的都处理好再去下一个)。
在解决迷宫问题的时候,我们得灵活运用这些算法呀。
要是只用一种方法,那多死板呀!比如说有些迷宫特别复杂,就得用广度优先搜索来慢慢地、稳稳地探索。
不然你一头扎进去,说不定就在里面打转出不来喽(那不就像在大雾天开车,没有个好的指引可不行)!
总之呢,迷宫算法真的超级有趣又超级实用!大家一定要好好去研究研究哦!相信你们会发现其中的奇妙之处的!。
算法之迷宫问题
算法之迷宫问题题⽬:给⼀个⼆维列表,表⽰迷宫(0表⽰通道,1表⽰围墙)。
给出算法,求⼀条⾛出迷宫的路径。
解决思路:在⼀个迷宫节点(x,y)上,可以进⾏四个⽅向的探查:maze[x-1][y], maze[x+1][y], maze[x][y-1], maze[x][y+1]思路:从⼀个节点开始,任意找下⼀个能⾛的点,当找不到能⾛的点时,退回上⼀个点寻找是否有其他⽅向的点。
⽅法:创建⼀个空栈,⾸先将⼊⼝位置进栈。
当栈不空时循环:获取栈顶元素,寻找下⼀个可⾛的相邻⽅块,如果找不到可⾛的相邻⽅块,说明当前位置是死胡同,进⾏回溯(就是讲当前位置出栈,看前⾯的点是否还有别的出路)⽤栈实现:maze = [[1,1,1,1,1,1,1,1,1,1],[1,0,0,1,0,0,0,1,0,1],[1,0,0,1,0,0,0,1,0,1],[1,0,0,0,0,1,1,0,0,1],[1,0,1,1,1,0,0,0,0,1],[1,0,0,0,1,0,0,0,0,1],[1,0,1,0,0,0,1,0,0,1],[1,0,1,1,1,0,1,1,0,1],[1,1,0,0,0,0,0,0,0,1],[1,1,1,1,1,1,1,1,1,1]]maze = [[1,1,1,1,1,1,1,1,1,1],[1,0,0,1,0,0,0,1,0,1],[1,0,0,1,0,0,0,1,0,1],[1,0,0,0,0,1,1,0,0,1],[1,0,1,1,1,0,0,0,0,1],[1,0,0,0,1,0,0,0,0,1],[1,0,1,0,0,0,1,0,0,1],[1,0,1,1,1,0,1,1,0,1],[1,1,0,0,0,0,0,0,0,1],[1,1,1,1,1,1,1,1,1,1]]dirs = [lambda x,y:(x-1,y), #上lambda x,y:(x,y+1), #右lambda x,y:(x+1,y), #下lambda x,y:(x,y-1), #左]def solve_maze(x1, y1, x2, y2):stack = []stack.append((x1,y1))maze[x1][y1] = 2while len(stack) > 0: # 当栈不空循环cur_node = stack[-1]if cur_node == (x2,y2): #到达终点for p in stack:print (p)return Truefor dir in dirs:next_node = dir(*cur_node)if maze[next_node[0]][next_node[1]] == 0: #找到⼀个能⾛的⽅向⽤队列实现:解决思路:思路:从⼀个节点开始,寻找所有下⾯能继续⾛的点。
Dijkstra算法初步-迷宫问题
Dijkstra算法初步-迷宫问题你来到⼀个迷宫前。
该迷宫由若⼲个房间组成,每个房间都有⼀个得分,第⼀次进⼊这个房间,你就可以得到这个分数。
还有若⼲双向道路连结这些房间,你沿着这些道路从⼀个房间⾛到另外⼀个房间需要⼀些时间。
游戏规定了你的起点和终点房间,你⾸要⽬标是从起点尽快到达终点,在满⾜⾸要⽬标的前提下,使得你的得分总和尽可能⼤。
现在问题来了,给定房间、道路、分数、起点和终点等全部信息,你能计算在尽快离开迷宫的前提下,你的最⼤得分是多少么?Input第⼀⾏4个整数n (<=500), m, start, end。
n表⽰房间的个数,房间编号从0到(n - 1),m表⽰道路数,任意两个房间之间最多只有⼀条道路,start和end表⽰起点和终点房间的编号。
第⼆⾏包含n个空格分隔的正整数(不超过600),表⽰进⼊每个房间你的得分。
再接下来m⾏,每⾏3个空格分隔的整数x, y, z (0<z<=200)表⽰道路,表⽰从房间x到房间y(双向)的道路,注意,最多只有⼀条道路连结两个房间, 你需要的时间为z。
输⼊保证从start到end⾄少有⼀条路径。
Output⼀⾏,两个空格分隔的整数,第⼀个表⽰你最少需要的时间,第⼆个表⽰你在最少时间前提下可以获得的最⼤得分。
Input⽰例3 2 0 21 2 30 1 101 2 11Output⽰例21 6---------------------------------------------------我是分割线^_^-------------------------------------------------此题为迪杰斯特拉算法的基础题,⽤来了解单源最短路径的求法,其实⼀开始的⼊门都是在邻接表和优先队列做,但《啊哈!算法》这本书中看到的,最短路径问题,这个题可以使⽤邻接表和优先队列做,但《啊哈!算法》这本书中看到的,最短路径问题,这个题可以使⽤我都没⽤,我⽤了效率⽐较低下的⼆维数组,不过也有被难倒的地⽅,就是在计算最⼤分数的时候,被卡了有⼏个⼩时都找不到错误后来发现原来是因为相等的情况多余了,要注意最后来发现原来是因为相等的情况多余了,要注意最的时候,被卡了有⼏个⼩时都找不到错误⼩最⼤和最长最短的⼀些限制条件,以后写的时候要提醒⾃⼰注意= =。
数据结构与算法之迷宫
数据结构预算法—迷宫问题哈尔滨工业大学金字塔的小蜗牛一、问题描述:编写一个程序:随机生成一个20 x 20的迷宫,并找到一条从入口到出口的路线。
要求:(1)“迷宫”大小可变,模式随机。
(2)迷宫没有通路时,给出警告;有通路时,任给一条具体路径。
(3)分析算法的效率。
二、算法基本思想:通过产生随机数的方式生成一个只含0和1的二维数组,用这个二维数组来表示迷宫,其中数字1表示通路,数字0表示围墙。
设定好迷宫的入口和出口,采用递归的方法找到一条走出迷宫的路线图,数组my_maze存有正寻找的路线图,在找路线图的运行过程中没有出口的路线图即被后来的路线覆盖,直到找到能走出迷宫的路线图,然后将my_maze中的元素赋给target_maze。
最终将路线图target_maze打印出来。
三、主要数据结构(编程环境:code blocks):1、二维数组一个表示迷宫的二维数组maze[i][j],相关操作有:(1)建立二维数组,赋初值。
(2)数组以指针形式在各函数中传递。
(3)改变数组中某个元素的值。
2、指针运算函数move_to、print_maze1、print_maze2都含有指针作为函数中的变量进行调用。
3、递归运算在寻找迷宫路径的时候,用到了递归运算。
在程序中即从一个点搜寻下一个点的反复过程中不断调用move_to函数自身,最终寻找到走出迷宫的路径。
四、主要函数功能:1、produce_maze()函数功能:随机生成一个20 x 20的二维迷宫。
2、move_to(int _i,int _j, int (*in_maze)[MAX_COL], int count)函数功能:采用递归思想找到一条走出迷宫的路径。
3、print_maze1(int (*maze)[MAX_COL])函数功能:打印出生成的迷宫图案。
4、print_maze2(int (*maze1)[MAX_COL],int (*maze2)[MAX_COL])函数功能:打印出附有路径的迷宫图案。
python迷宫最短路线算法 -回复
python迷宫最短路线算法-回复Python迷宫最短路径算法在计算机科学领域中,迷宫最短路径问题是一个经典的算法挑战。
给定一个迷宫,目标是找到从起点到终点的最短路径。
Python作为一种强大的编程语言,提供了丰富的数据结构和算法库,为解决这个问题提供了很多选择。
在本文中,我们将一步一步地介绍一个基于Python的迷宫最短路径算法。
第一步:定义迷宫数据结构首先,我们需要定义迷宫的数据结构。
迷宫通常由网格组成,每个网格可以是一个单元格。
我们可以使用二维数组来表示迷宫,其中每个值代表不同的状态。
例如,0表示通行,1表示墙壁,2表示起点,3表示终点。
在Python中,可以使用列表嵌套列表来表示二维数组。
下面是一个简单的迷宫示例:maze = [[1, 1, 1, 1, 1, 1, 1],[1, 0, 0, 0, 0, 0, 1],[1, 1, 1, 1, 1, 0, 1],[1, 0, 0, 0, 1, 0, 1],[1, 0, 1, 0, 0, 0, 1],[1, 0, 1, 1, 1, 1, 1],[1, 1, 1, 1, 1, 1, 1]]在这个示例中,1表示墙壁,0表示通行,2代表起点,3代表终点。
这个迷宫是一个7x7的网格。
第二步:使用广度优先搜索算法寻找最短路径接下来,我们将使用广度优先搜索算法(BFS)来找到从起点到终点的最短路径。
BFS是一种图遍历算法,它从起点开始,逐层遍历,直到找到目标节点。
首先,我们需要定义一个队列,用于存储待处理的节点。
我们可以使用Python的deque数据结构来表示队列。
然后,我们设定起点为当前节点,并将其加入队列中。
我们还需要定义一个字典来保存每个节点的父节点,以便在找到最短路径时进行回溯。
下面是使用BFS算法来找到最短路径的Python代码示例:pythonfrom collections import dequedef find_shortest_path(maze, start, end):# 创建队列queue = deque()# 将起点加入队列queue.append(start)# 创建字典,用于记录每个节点的父节点parent = {}parent[start] = None# 广度优先搜索while queue:current = queue.popleft()if current == end:break# 获取当前节点的相邻节点neighbors = get_neighbors(maze, current)for neighbor in neighbors:if neighbor not in parent:parent[neighbor] = currentqueue.append(neighbor)# 回溯最短路径path = []while current:path.append(current)current = parent[current]return list(reversed(path))在这段代码中,我们首先定义了一个帮助函数`get_neighbors(maze, current)`,用于获取当前节点的相邻节点列表。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第1章迷宫算法1.0.1 迷宫坐标和绝对方向的建立迷宫是由16*16大小的方格组成的,其行列各有16个方格.为了让电脑鼠记住所走过的各个迷宫的信息,我们就要对这256个迷宫格进行编号.很明显,用坐标是非常方便的.那么,我们规定,以电脑鼠放到起点时的方向为参照,此时电脑鼠的正前方为Y轴正方向,后方为Y轴负方向,左方为X轴负方向,右方为X轴正方向.迷宫格与坐标的对应关系如图3-1所示.图3- 1 迷宫格与坐标对应关系根据坐标的定义和比赛规则可以知道,电脑鼠的起点可能在(0,0)点,也可能在(F,0)点.即终点可能在电脑鼠的右前方,也可能在电脑鼠的左前方.这个可以根据电脑鼠第一次检测到的转弯口是在左方还是右方判断出来.如图3-2所示,如果电脑鼠是从(0,0)点出发,那么它第一个检测到的拐弯口是在它的右边,如果电脑鼠是从(0,F)出发的,那么它第一个检测到的拐弯口在它的左边.图3-2 迷宫的方向值定义为了把上下左右这四个方向参数转换为微控制器能够识别的符号,我们将向上方向定义为0,向右方向定义为1向左定义为3.如图3-2所示.1.0.2 相对方向与绝对方向的转换有了坐标和方向后,电脑鼠在迷宫中行走就可以随时知道自己所处的位置和方位了,然而,对于电脑鼠来说,红外线传感器的位置和方向是固定不变的,而对于迷宫来说,红外线传感器的位置和方向是随着电脑鼠前进方向的变化而一起变化的,这就是由于选择参照物的不同而出现的差异,由此引出了两个方向的问题:相对方向和绝对方向.相对方向:以电脑鼠当前行走方向为参照的方向,称为相对方向.绝对方向:以迷宫绝对坐标平面为参照的方向,称为绝对方向.那么传感器所检测到的资料如何存储才能更利于处理呢?很明显,若以相对方向存储的资料将会是混乱一团的,不仅要存储麻烦,而且要记录起检测该资料时电脑鼠所处的方向,存储量也比较大,处理起来也非常麻烦,而以绝对方向存储的资料将不必考虑电脑鼠当时的方向即可进行处理,非常方便.这样,就会经常遇到相对方向与绝对方向的互换,我们以变量Dir记录电脑鼠前进方向上的绝对方向值,即电脑鼠前言的绝对方向值始终为Dir,这样电脑鼠的相对方向转换为绝对方向如表3-1所示.表3-1 相对方向转换为绝对方向相对方向绝对方向电脑鼠前方Dir电脑鼠右方(Dir+1)%4电脑鼠后方(Dir+2)%4电脑鼠左方(Dir+3)%4例如,若电脑鼠当前的前进方向为迷宫的上方,即当时Dir=0,由表3-1可以计算出其相对方向的右、后、左三方向的绝对方向值为:1、2、3.这三个值分别代表迷宫绝对方向的右方、下方和左方.可以看出,此时电脑鼠的相对方向与绝对方向相同.再假如当前电脑鼠前进方向为迷宫绝对方向的左方,即当时Dir=3,由表3-1可以计算出其相对方向右、后、左三个方向的绝对方向值为:0、1、2.可知,这三个值分别代表迷宫绝对方向的上方、右方、下方.可以看出,此时电脑鼠的前方为迷宫的左方,电脑鼠的右方为迷宫的上方,电脑鼠的后方为迷宫的右方.有时系统的还需要根据绝对方向求出相对方向,比如要控制电脑鼠转向某一个绝对方向,这时就需要计算出该绝对方向处于电脑鼠的哪个相对方向上,电脑鼠根据相对方向来决定转向.首先,根据目标的绝对方向(Dir_dst)和当前的绝对方向(Dir)求出方向偏差值(△Dir),如式6-1所示.△Dir = Dir_dst – Dir (6-1)为了使△Dir的值落在0-3的范围内,计算式改进为:△Dir = (Dir_dst + 4 - Dir)%4 (6-2)这时就可以根据方向偏差值求出电脑鼠的相对方向,如表3-2 绝对方向转换为相对方向所示.表3-2 绝对方向转换为相对方向绝对方向差值(△Dir)相对方向0电脑鼠前方1电脑鼠右方2电脑鼠后方3电脑鼠左方1.0.3 坐标转换假设电脑鼠已知当前位置坐标(X,Y),那么就可以求出其某一绝对方向上(相对方向也可按表3-1转换为绝对方向)的相邻坐标值,如所示.该表是可逆的,即可以根据坐标值的变化求出绝对方向.表3-3 坐标转换绝对方向坐标当前位置(X,Y)上方 (0)(X,Y+1)右方 (1)(X+1,Y)下方 (2)(X,Y-1)左方 (3)(X-1,Y)1.0.4 墙壁资料储存当电脑鼠达到一方格坐标时,应根据传感器检测结果记录下当前方格的墙壁资料,为了方便管理和节省储存空间,每一个字节变量的低四位分别用来储存一个方格四周的墙壁资料,如表3-4所示,迷宫共有16 * 16 个方格,所以可以定义一个16 * 16 的二维数组变量来保存整个迷宫墙壁资料。
迷宫墙壁资料全部初始化为0,凡是走过的迷宫格至少有一方没有墙壁,即墙壁资料不为0。
这样就可以通过单元格存储的墙壁资料是否为0来确定该单元格是否曾搜索过。
表3-4 墙壁资料存储方式变量位代表的绝对方向备注bit 0上方 1 :有路,0 :有墙壁bit 1右方 1 :有路,0 :有墙壁bit 2下方 1 :有路,0 :有墙壁bit 3左方 1 :有路,0 :有墙壁bit 7- bit 4保留位1.1 迷宫搜索方法在没有预知迷宫路径的情况下,电脑鼠必须要先探索迷宫中的所有迷宫格,直到抵达终点为止。
做这个处理的电脑鼠要随时知道自己的位置及姿势,同时要记录下所有访问过的方块四周是否有墙壁。
在搜索过程中,还要尽量避免重复搜索已知搜索过的地方。
因此,迷宫搜索有两种方式:1. 尽快到达目标地。
2. 搜索整个迷宫。
利用第一种方式虽然可以缩短搜索迷宫所需的时间,但不一定能够得到整个迷宫的地图资料。
若找到的路不是迷宫的最优路径,这将会影响电脑鼠最后冲刺的时间。
若采用第二种方式,可以得到整个迷宫的地图资料,这样就可以求出最优路径。
不过采用这种方法所花费的搜索时间较长。
1.2 深度优先搜索算法(DFS)迷宫问题也可以看作典型的图搜索问题,常用的图搜索策略有:深度优先搜索算法(DFS)、广度优先搜索算法(BFS)。
深度优先搜索算法递归定义,先访问出发顶点(并标记为已访问),然后以深度优先搜索算法依次访问他的尚未访问过的各个邻接顶点,在访问过程中,表现出纵向深入的特点。
深度优先搜索利用堆栈来实现,可以找到一条从起点到终点的路径,但不一定是最短路径。
广度优先搜索算法是一种按层遍历算法,先访问出发顶点(并标记为己访问),然后依次访问它没有访问过的各个邻接顶点,再按相同的规则,访问各个邻接顶点的尚未访问过的各个邻接顶点,在访问的过程中,表现出横向扩展的特点。
利用列队的广度优先搜索算法,通常可以找到最短路径。
深度优先搜索算法遵循的策略是尽可能深的搜索图。
在此算法中,对于新发现的节点,如果他还有以此为起点而尚未探测的边,就沿此边继续搜索下去,当节点V的所有边都以搜索过,搜索将回溯到发现结点V 的那条边的始结点。
这一过程一直持续到从原结点到所有可搜索的结点都被搜索过为止。
如果还存在未被发现的结点,则选择其中一个作为源结点并重复以上过程,整个过程重复直到所有结点都被发现为止。
图3-3是一个简单的深度优先搜索策略算法示意图。
图3-3 深度优先搜索示意图路径搜索的选择:按照八个方向来搜索迷宫,会有更好的效率,为了更方便的分析,我们简化问题,只采用四个方向来搜索迷宫,如下表3- 5所示:表3- 5 四方向搜索(i-1,j)(i,j-1)(i,j)(i,j+1)(i+1,j)图3-4 简单迷宫深度优先搜索示实例从图3-4示意图可以看出,在深度优先搜索策略中,我们首先扩展最新产生的结点(也即最深的结点),深度相同的结点可以任意排列。
可以如下定义结点的深度:(1) 起始结点(根节点)深度定义为0;(2) 其他任何结点的深度在其父结点的深度上加1。
首先,扩展最深的结点使得搜索沿着空间中某条单一路径从起始单元格到达目标单元格进行下去。
只有当搜索到没有后续结点的状态时,才考虑用另一个替代的路径。
替代路径与前面刚搜索过的路径的不同之处在于后面的n步不同,而这个n应保持尽可能的最小。
1.3 迷宫搜寻法则设定搜寻法则和策略是为了电脑鼠可以以最快的方式找到终点,到达目标后随即以所走过的路径中找出一路径返回起点,然后再做冲刺,直达目的;法则的设定很重要,它可以使电脑鼠不多走冤枉路,可节省很多时间而制胜。
每一只电脑鼠到达一方格时它最多有三个方向可前进,最少则因为三面都有墙,没有可以前进的方向;当遇到二个以上的可选择方向时,由于不同场合需要而有不同优先搜寻的方向顺序,常见的法则有以下几种:1. 右手法则:遇叉路时,以右边为优先前进方向,然后直线方向,左边方向;2. 左手法则:遇叉路时,以左边为优先前进方向,然后直线方向,右边方向;3. 中左法则:与右手法则相似,不过方向选择顺序改为直线优先,然后左边,右边;4. 中右法则:遇叉路时,以直线为优先前进方向,然后右边方向,左边方向;5. 求心法则:遇叉路时,以距中心最短的那个方向优先,然后依次选择。
6. 乱数法则:以电脑的随即值作为下一前进方向。
1.4 迷宫搜寻策略迷宫搜寻模式有全迷宫搜寻策略和部分迷宫搜寻策略两种:1. 全迷宫搜寻策略:电脑鼠以任一搜寻法则前进到达终点后,电脑鼠会反身继续前进,然后以原设定的搜寻法则,时时检查未走过的路,直到每一方格都搜寻过后,才回起点。
2. 部分迷宫搜寻策略:电脑鼠以任一搜寻法则前进到达终点后,电脑鼠将沿原路线返回起点,不再进行其它搜寻。
如果不计算搜寻时间,可采用全迷宫搜寻策略,待地毯式的搜寻过所有方格后,再计算最佳路径,作最后的冲刺,冲刺成绩一定相当不错。
由于考虑搜寻时间,因此我们必须考虑部分迷宫搜寻策略,甚至还可能须考虑加入求心法则,截路径功能等更智慧的法则来协助;此时找到的路径可能不是最佳路径,但保证花的时间最短。
1.5 寻找最优路径的方法及等高图的应用假设电脑鼠已经搜索完整个迷宫或者只搜索了包含起点和终点的部分迷宫,且记录了已走过的每个迷宫格的墙壁资料,因此,引入等高图的概念和制作方法。
等高图就是等高线地图的简称,有如一般地图可以标出同一高度的地区范围一样,等高图运用在迷宫地图上,可以标出每个迷宫格到起点相等步数的关系,可不要小看它,许多封闭路径的逃脱与冲刺的关卡都可在我们制作出等高图后迎刃而解,使电脑鼠更容易逃脱,少走一结弯路。
1.5.1 等高图制作原理首先开辟一块16*16的二维数组空间(MapStep[16][16]),其中每一个元素代表迷宫中的一个方格,用以计算后储存各方格至起点的最短路径步数(所谓步数即为路径中经过的方格数).当起点坐标处标识为1时,可以直接达到的相邻方格均为2,再远的方格的等高值依次递增.这样距离越远的地方等高值越大.1.5.2 等高图制作步骤用一个4*4的小迷宫作为范例,并以坐标(0,0)作为起点.步骤一:1. 把所有迷宫格上的等高值填为0xff.(0xff是迷宫中等高值的上限)2. 起点坐标(0,0),其步数填入13. 记录等高值的变量Step自加1变为24. 在堆栈中存入起点坐标(0,0)步骤二:1. 观察图3. 3中(0,0)点四周仅有一个方向可前进(可前进的定义是说箭头所指方向的步数值比当前坐标上的值大2以上,如坐标(0,1)的值为0xff,则0xff-1>1,则坐标(0,1)为可前进的方向)2. 进入箭头所指方向的方格,此时坐标为(0,1)3. 填入步数值Step4. Step = Step + 1图3-5 等高图步骤一图3-6 等高图步骤二步骤三:1. 图3. 4 中(0,1)点四周有两个可前进的方向,即此坐标点为一岔路.2. 将该岔路坐标存入堆栈3. 进入箭头所指的一方向(可任选之,对结果无影响),此时坐标为(1,1)4. 填入Step值5. Step = Step + 16. 得到下图图3-7 等高图步骤三步骤四:1. 图3. 5 中仅有一个可前进的方向2. 进入箭头所指方向,此时坐标为( 1 , 0)3. 填入Step 值4. Step = Step + 15. 得到下图图3- 8 等高图步骤四步骤五:1. 图3. 6 中仅有一个可前进的方向2. 进入箭头所指方向,此时坐标为( 2 , 0)3. 填入Step值4. Step = Step + 15. 得到下图图3-9 等高图步骤五步骤六:1. 图3. 7 中四周无可前进的方向2. 取出堆栈中的内容,跳到其保存的坐标( 1 , 0)3. 读出对应的坐标的Step值,Step = 24. Step = Step + 15. 得到下图图3-10 等高图步骤六步骤七:1. 图3. 8 中坐标(0 , 1)点有一个前进方向( 坐标 (0,0) 处Step 值为1),不比当前坐标的步数值大2以上,所以不是可前进的方向.2. 进入箭头所指方向,此时坐标为(0 , 2)3. 填入Step值4. Step = Step + 15. 得到下图图3-11 等高图步骤七步骤八:1. 图3. 9 中坐标 (0 , 2)有两个可前进的方向2. 将该岔路坐标入栈3. 进入箭头所指的一个方向,此时坐标为(1 , 2)4. 填入Step值5. Step = Step + 16. 得到下图图3-12 等高图步骤八以此类推,直到当前坐标没有可前进方向,且堆栈中没有示处理完的分岔点时结束.最终可以得到如下图所示的等高图.等高图的数字即为步数,也就是代表其相对应的位置,距离起点的最少方格数,如坐标(2 , 2)距离起点的有5格,坐标(3 , 2)距离起点有8格.图3-13 等高图最终的示意图1.5.3 转弯加权的等高图制作由于电脑鼠转弯要浪费一定时间,如图3. 11 所示,虽然(0 , 2)点和(1 , 1)点的等高值都为3,但肯定我们会认为电脑鼠从( 0 , 2)点到起点比从( 1 , 1)点到起点要快.因此,为了寻找一条最优的路径(也就是能最快达到路径),可以给转弯点加权.假设权值为1,即经过转变前进的坐标的等高值是由当前等高值加2得到的.加权后的等高图如下图所示,加权值可以根据自己电脑鼠转弯性能来决定.这里我们设置为1.图3-14 转弯加权后的等高图1.6 软件结构框架电脑鼠在运行中可以划分为四个状态 : 等待状态、启动状态、搜索迷宫状态和冲刺状态.1. 等待状态在该状态中,电脑鼠静止在起点,等待开始按键的按下,同时实时显示传感器检测结果,这样方便调试传感器的灵敏度. 当控制启动的按键按下后,电脑鼠进入启动状态.2. 启动状态在该状态中,电脑鼠根据第一次转弯的方向判断起点是在坐标的(0 , 0)点还是( 15 , 0)点,其程序流程图见下图所示.图3-15 判断起点坐标程序流程图3. 搜索迷宫状态在该状态中,电脑鼠的任务就是探索并记忆迷宫地图.这里采用右手搜索法则,并搜索全迷宫.其程序流程图见下图.图3-16 迷宫搜索流程图4. 冲刺状态迷宫搜索完毕后,根据算法找出一条最优路径冲刺到终点.冲刺结束后返回到起点.。