迷宫问题

来源:互联网 发布:nginx配置不同的tomcat 编辑:程序博客网 时间:2024/06/09 22:23

代码链接:github

迷宫

如下图所示,在一个矩阵里面,0代表通路,1代表墙。 image image构成一个简单的迷宫


问题:

  • 1.那么怎样用程序来寻找迷宫的出口呢?
  • 2.如果有多个出口,如何寻找一条最短通路?
  • 3.如果迷宫里面存在环路,那么又如何寻找最短通路?

找通路之前

解决问题1

思路

首先需要得到这个迷宫,可以是程序运行时从文件中读取,也可以通过数组把迷宫保存起来。然后自定义一个类型存放可以通的结点坐标。找通路的的思想主要是下面三步:

1.试探

使用两个结点,一个结点表示当前位置,另一个结点表示下一步位置,并按一定的顺序(如顺时针)从起点开始试探下一个相邻结点是否可以通,若发现下一个结点是0,则把当前结点走到下一个位置,并把走过的结点放到栈里,方便回溯。

2.标记

走一步,先把上一步节点标记成2,表示已经走过了,防止试探时发生死循环,直到出口结束,或者走到死胡同,走到死胡同则回溯。

3.回溯

走到死胡同后,可以取出栈顶元素赋给当前位置,等于是走到死胡同,再往回走,看看以前走过的岔路口,还有没有没有试探过的路口,如果有继续试探,若没有继续出栈回溯,直到栈为空,表示走到入口,说明没有通路。把走过的回头路标记为3.

主要代码

bool CheckIsAccess(Pos pos){if (pos._col >= 0 && pos._col<10&&pos._row<10&&\pos._row >= 0 && _maze[pos._row][pos._col] == 0){return true;}return false;}//普通方法寻找通路bool GetPath(Pos entry){stack<Pos> s;s.push(entry);Pos cur = entry;Pos next = entry;while (!s.empty()){if (cur._row == M-1){_maze[cur._row][cur._col] = 2;printf("找到出口:[%d , %d]\n",cur._row, cur._col);//return true;}//上next = cur;next._row = cur._row - 1;if (CheckIsAccess(next)){_maze[cur._row][cur._col] = 2;s.push(cur);cur._row = next._row;continue;}//右next = cur;next._col = cur._col + 1;if (CheckIsAccess(next)){_maze[cur._row][cur._col] = 2;s.push(cur);cur._col = next._col;continue;}//下next = cur;next._row = cur._row + 1;if (CheckIsAccess(next)){_maze[cur._row][cur._col] = 2;s.push(cur);cur._row = next._row;continue;}//左next = cur;next._col = cur._col - 1;if (CheckIsAccess(next)){_maze[cur._row][cur._col] = 2;s.push(cur);cur._col= next._col;continue;}_maze[cur._row][cur._col] = 3;cur = s.top();s.pop();}return false;}

如何求最短路径

解决问题2

  • 利用栈求最短路径

首先在保存一个全局的栈和一个记录每次走的节点的栈(我叫他记录栈),开始试探寻找通路,并把走过的节点放入记录栈,找到其中一条通路,如果全局栈为空就把记录栈的数据赋给全局栈,若全局栈不为空,则比较两栈的大小,把全局栈替换为小的。然后回溯,继续寻找其他方向的通路,知道走回起点,即栈为空。

主要代码

//解决带环的试探下一个节点bool CheckIsAccess(Pos cur, Pos next){if (next._col<N&&next._col>0 && next._row<M&&next._row>0\&& (_maze[next._row][next._col] == 0 || _maze[next._row][next._col] > _maze[cur._row][cur._col])){return true;}return false;}//用栈寻找最短通路void StackGetShortPath(Pos entry, stack<Pos>& s, list<Pos>& shortpath){s.push(entry);Pos cur = entry;Pos next = entry;while (!s.empty()){//进入入口开始试探下一个节点,若可以进入下一个节点,这说明没有到死胡同也没有到出口//上next = cur;next._row = cur._row - 1;if (CheckIsAccess(next)){_maze[cur._row][cur._col] = 2;s.push(cur);cur._row = next._row;continue;}//右next = cur;next._col = cur._col + 1;if (CheckIsAccess(next)){_maze[cur._row][cur._col] = 2;s.push(cur);cur._col = next._col;continue;}//下next = cur;next._row = cur._row + 1;if (CheckIsAccess(next)){_maze[cur._row][cur._col] = 2;s.push(cur);cur._row = next._row;continue;}//左next = cur;next._col = cur._col - 1;if (CheckIsAccess(next)){_maze[cur._row][cur._col] = 2;s.push(cur);cur._col = next._col;continue;}//上下左右都试探完,判断是否走到出口if (cur._row == M - 1 || cur._row == 0){_maze[cur._row][cur._col] = 2;if (shortpath.empty() || s.size()< shortpath.size()){shortpath.clear();stack<Pos> tmp(s);while (!tmp.empty()){shortpath.push_front(tmp.top());tmp.pop();}}}//走到这说明走到死胡同,则进行回溯。_maze[cur._row][cur._col] = 3;cur = s.top();s.pop();}}
  • 递归求最短路径

利用递归也是利用一个栈来记录走过的节点,但是把递归的返回条件是发现四个方向都不能走时就返回(包括走到出口),最终可以把所有可以走的节点都遍历一遍,然后回到入口的位置。同样每次走到一个出口的时候用记录栈和全局栈比较,把全局栈来替换成小的通路。

解决问题3


带环问题如何求最短路径?如上图2

但是上面的求解最短路径的方法在遇到带环的迷宫时存在问题,因为带环的迷宫两条通路会共用一个出口(如上图2),也就是两条通路只能找到一次出口,所以记录栈只记录了其中一个通路,所以有可能找不到最短通路。要想把共用一个出口的两个通路都记录一遍,则有以下两种办法。

解决办法

  • 递归解决带环问题

同样按照上面找通路的方法寻找出口,不同的是改变标记的方法,用数字累加的方法记录走过的结点,试探的判断条件是下一个结点比当前位置的值大就可以走,(1是墙,防止把第二个位置累加到1,所以将入口改成2).看下面图理解如何实现的。

image

主要代码

//带环迷宫void GetShortPath(Pos entry, stack<Pos>& path, list<Pos>& shortpath){Pos cur = entry;Pos next = cur;path.push(cur);//上next = cur;next._row -= 1;if (CheckIsAccess(cur, next)){_maze[next._row][next._col] = _maze[cur._row][cur._col] + 1;GetShortPath(next, path, shortpath);}//右next = cur;next._col += 1;if (CheckIsAccess(cur, next)){_maze[next._row][next._col] = _maze[cur._row][cur._col] + 1;GetShortPath(next, path, shortpath);}//下next = cur;next._row += 1;if (CheckIsAccess(cur, next)){_maze[next._row][next._col] = _maze[cur._row][cur._col] + 1;GetShortPath(next, path, shortpath);}//左next = cur;next._col -= 1;if (CheckIsAccess(cur, next)){_maze[next._row][next._col] = _maze[cur._row][cur._col] + 1;GetShortPath(next, path, shortpath);}    //上下左右都试探完,判断是否走到出口if (cur._row == M - 1||cur._row == 0){if (shortpath.empty() || path.size()< shortpath.size()){shortpath.clear();stack<Pos> tmp(path);while (!tmp.empty()){shortpath.push_front(tmp.top());tmp.pop();}}}    //走到这说明走到死胡同,则进行回溯path.pop();}
  • 利用栈也可以解决

思想和递归的思想完全一样,只不过回溯的方式是利用pop栈顶元素来回溯。

原创粉丝点击