编程练习:走迷宫问题
2016-10-02 23:07
190 查看
问题:
实现给定迷宫,给出你认为最快的走出迷宫步数和策略;若不存在,请输出0。解决思路:
首先根据网上搜索资料,此问题可以看成是树的遍历问题,而典型的树的遍历可分为深度优先遍历和广度优先遍历,不同的遍历方式各有优缺点:摘录自:[一、深度优先搜索和广度优先搜索的深入讨论)]http://www.cnblogs.com/XBWer/archive/2012/06/20/2556558.html)
(一)深度优先搜索的特点是:
(1)无论问题的内容和性质以及求解要求如何不同,它们的程序结构都是相同的,即都是深度优先算法(一)和深度优先算法(二)中描述的算法结构,不相同的仅仅是存储结点数据结构和产生规则以及输出要求。
(2)深度优先搜索法有递归以及非递归两种设计方法。一般的,当搜索深度较小、问题递归方式比较明显时,用递归方法设计好,它可以使得程序结构更简捷易懂。当搜索深度较大时,当数据量较大时,由于系统堆栈容量的限制,递归容易产生溢出,用非递归方法设计比较好。
(3)深度优先搜索方法有广义和狭义两种理解。广义的理解是,只要最新产生的结点(即深度最大的结点)先进行扩展的方法,就称为深度优先搜索方法。在这种理解情况下,深度优先搜索算法有全部保留和不全部保留产生的结点的两种情况。而狭义的理解是,仅仅只保留全部产生结点的算法。本书取前一种广义的理解。不保留全部结点的算法属于一般的回溯算法范畴。保留全部结点的算法,实际上是在数据库中产生一个结点之间的搜索树,因此也属于图搜索算法的范畴。
(4)不保留全部结点的深度优先搜索法,由于把扩展望的结点从数据库中弹出删除,这样,一般在数据库中存储的结点数就是深度值,因此它占用的空间较少,所以,当搜索树的结点较多,用其他方法易产生内存溢出时,深度优先搜索不失为一种有效的算法。
(5)从输出结果可看出,深度优先搜索找到的第一个解并不一定是最优解。
二、广度优先搜索法的显著特点是:
(1)在产生新的子结点时,深度越小的结点越先得到扩展,即先产生它的子结点。为使算法便于实现,存放结点的数据库一般用队列的结构。
(2)无论问题性质如何不同,利用广度优先搜索法解题的基本算法是相同的,但数据库中每一结点内容,产生式规则,根据不同的问题,有不同的内容和结构,就是同一问题也可以有不同的表示方法。
(3)当结点到跟结点的费用(有的书称为耗散值)和结点的深度成正比时,特别是当每一结点到根结点的费用等于深度时,用广度优先法得到的解是最优解,但如果不成正比,则得到的解不一定是最优解。这一类问题要求出最优解,一种方法是使用后面要介绍的其他方法求解,另外一种方法是改进前面深度(或广度)优先搜索算法:找到一个目标后,不是立即退出,而是记录下目标结点的路径和费用,如果有多个目标结点,就加以比较,留下较优的结点。把所有可能的路径都搜索完后,才输出记录的最优路径。
(4)广度优先搜索算法,一般需要存储产生的所有结点,占的存储空间要比深度优先大得多,因此程序设计中,必须考虑溢出和节省内存空间得问题。
(5)比较深度优先和广度优先两种搜索法,广度优先搜索法一般无回溯操作,即入栈和出栈的操作,所以运行速度比深度优先搜索算法法要快些。
总之,一般情况下,深度优先搜索法占内存少但速度较慢,广度优先搜索算法占内存多但速度较快,在距离和深度成正比的情况下能较快地求出最优解。因此在选择用哪种算法时,要综合考虑。决定取舍。
代码实现及参考
对于此问题的搜集资料,一般实现用BFS(宽度优先搜索算法(又称广度优先搜索)算法,主要参考Fei Guo的《每天一道算法题】走迷宫》(主要参考算法流程及测试用例)和i逆天耗子的《C++ - 蓝桥杯 - 算法提高 学霸的迷宫 (bfs+记录路径) 》(主要参考算法流程及寻路策略的输出)
问题描述及测试用例:
输入描述:
输入包含多组数据。
每组数据包含一个10*10,由“#”和“.”组成的迷宫。其中“#”代表墙;“.”代表通路。
入口在第一行第二列;出口在最后一行第九列。
从任意一个“.”点都能一步走到上下左右四个方向的“.”点。
输出描述:
对应每组数据,输出从入口到出口最短需要几步。
输入例子:
#.######## #........# #........# #........# #........# #........# #........# #........# #........# ########.#
#.######## #........# ########.# #........# #.######## #........# ########.# #........# #.######.# ########.#
以下为编写的代码(C++实现)
/* 实现走迷宫算法 QiYe005 2016.10.2 首先实现深度优先遍历 深度优先遍历主要参考:http://www.cnblogs.com/LUO77/p/5813864.html 修改策略输出参考:http://blog.csdn.net/qq_34594236/article/details/51773620 加入自编泛型类+修改 */ #include<iostream> #include<exception> #include<vector> #include<algorithm> #include<time.h> #include<queue> using namespace std; //函数定义 //模板类matrix头文件定义 template<class T> class matrix { public: matrix(int rows = 1, int cols = 1, const T&value = T()); //构造函数 vector<T>&operator[](int i); //提领运算 const vector<T>&operator[](int i)const; //提领预算 int rows()const{ return row; } //矩阵行数 int cols()const{ return col; } //矩阵列数 void resize(int rows, int cols); //重置矩阵大小 void print_matrix(ostream&out)const; //矩阵输出 private: int row, col; //矩阵行数和列数 vector<vector<T>>mat; //向量表示的矩阵 }; //定义自己的异常 class MyMatIndUnboundException :public exception { virtual const char* what() const throw() { return "matrix index cross boarder!"; } }myExcept; //子函数实现功能 //构造函数 template<class T> matrix<T>::matrix(int rows, int cols, const T& value) :row(rows), col(cols), mat(rows, vector<T>(cols, value)) { } //提领函数 template<class T> vector<T>& matrix<T>::operator[](int i) { if (i < 0 || i >= row)throw myExcept; return mat[i]; } template<class T> const vector<T>& matrix<T>::operator[](int i) const { if (i < 0 || i >= row)throw myExcept; return mat[i]; } //重置矩阵大小 template<class T> void matrix<T>::resize(int rows, int cols) { if (rows == row&&cols == col) return; row = rows, col = cols; mat.resize(row); //重置行数 for (int i = 0; i < row; i++) mat[i].resize(col); //重置列数 } //打印矩阵元素 template<class T> void matrix<T>::print_matrix(ostream&out)const { for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { cout << mat[i][j] << " "; } cout << endl; } cout << endl; } //本题用结构体定义 struct pos { int x; int y; int level; string strategy; }; ////广度优先搜索子函数,伪代码见:http://baike.baidu.com/link?url=A3Ka5hoc9AngKUJ2oBUGNOQNcjO2AaMbo9bv0JPLId4B8spj_NO_2FF2OOtNqJ6YO8V1_oaB7bFAruA6NCctuuya8ms8YQ0UB-2UXrWcnm0XrFW4q1pEs4UD9dZDglcAGiYzEMwVlHBBm1l5pcoUpq void BFS(matrix<char>& MatMap, pos& startPoint,pos& endPoint,pos& resultPoint) { static const int dir[4][2] = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } }; static const char dircection[4] = { 'D', 'U', 'R', 'L' }; queue<pos> depthStack; int m = MatMap.rows(); int n = MatMap.cols(); //开辟等大小的bool型探索矩阵记录是否访问过该位置,默认false为未访问。 matrix<bool> visitedMat(m,n,false); depthStack.push(startPoint); visitedMat[startPoint.x][startPoint.y] = true; while (!depthStack.empty()) { pos currPos = depthStack.front(), nextPos; //弹出队列 depthStack.pop(); for (int i = 0; i < 4; i++) { //又当前值计算移动后的值 nextPos.x = currPos.x + dir[i][0]; nextPos.y = currPos.y + dir[i][1]; nextPos.level = currPos.level + 1; nextPos.strategy = currPos.strategy + dircection[i]; //若再走一步就可到达终点,则输出 if (nextPos.x == endPoint.x&&nextPos.y == endPoint.y) { resultPoint = nextPos; return; } //当移动后坐标在合法范围且可接近时,将其入队,并置位探索矩阵为已搜索 if (nextPos.x >= 0 && nextPos.x < m&&nextPos.y >= 0 && nextPos.y < n&&visitedMat[nextPos.x][nextPos.y] == false && MatMap[nextPos.x][nextPos.y] == '.') { depthStack.push(nextPos); visitedMat[nextPos.x][nextPos.y] = true; } } } resultPoint.level = 0; resultPoint.strategy = ""; } //主函数 int main(void) { try { const int inputRows = 10, inputCols = 10; matrix<char>charMat(inputRows, inputCols, 'A'); pos startPoint = { 0, 1, 0 }, endPoint = { 9, 8, 0 }; while (cin >> charMat[0][0]) { for (int i = 0; i < inputRows; i++) for (int j = 0; j < inputCols; j++) { if (i == 0 && j == 0) continue; cin >> charMat[i][j]; } pos resultPoint; BFS(charMat, startPoint, endPoint, resultPoint); //打印输出结果 cout << "需要" << resultPoint.level << "步走出迷宫" << endl; cout << "策略为" << resultPoint.strategy.c_str() << endl; return 0; } } catch (exception ex) { cout << ex.what() << endl; } }
更进一步
对于此类问题的更复杂情形,可以考虑A*算法,参见:A* Pathfinding for Beginners相关文章推荐
- 编程练习:走迷宫问题后续
- C Primer Plus 13章编程练习7问题
- 写给妹妹的编程札记 5 - 搜索: 迷宫问题 - 广度优先搜索
- 编程练习:赛车跑圈(走楼梯)问题
- 搜索练习3 /poj.org/problem 3984 迷宫问题/bfs 回溯找路经
- 编程练习A(回路问题)
- 编程练习-动态规划(生产线问题)
- 编程练习:N皇后问题 (JAVA)
- myEclipse Socket编程半双工练习问题
- GEEK编程练习— —计数问题
- HihoCoder1644 : 完美命名的烦恼([Offer收割]编程练习赛37)(有向图的一笔画问题||欧拉路)
- 笨笨熊搬家问题深入--迷宫问题--深度搜索--还是填充思想并且打印出路线--递归--结构体数组---编程随笔(4)
- 算法练习-NOJ-1009-迷宫问题
- 迷宫问题-----深度优先回溯算法C++编程练习
- 编程练习:同色方块识别问题
- SDAU 编程练习三 动态规划和动态规划与背包问题相结合的问题
- 写给妹妹的编程札记 4 - 搜索: 迷宫问题 - 深度优先搜索
- UFLDL矢量化编程练习:遇到问题
- 26-网络编程-15-网络编程(TCP协议-练习-常见问题)
- 编程练习:N皇后问题 (JAVA)