迷宫及走迷宫时的最优解

xiaoxiao2021-02-28  66

下面简单阐述下使用栈(循环)和使用递归来走迷宫的区别:

使用递归: 分为两个部分: 一部分为已走过的路径(通路) 另一问题是:递归的子问题(根据下一个可以通的位置继续查找出口)。 将走过的路径保存在栈帧内。 递归结束时返回上一层栈帧,销毁当前栈帧。

使用栈(保存走过的路径) 分为两个部分: 一部分为已走过的路径(通路),将其保存在栈内 另一部分为: 试探四个方向,沿着某个可以走通的方向一直试探下去,直到走不通时,退出栈顶元素,栈顶元素始终为当前所处的位置。

1.递归走迷宫: (利用回溯法)

#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; #include<cassert> struct Pos//设置坐标 { Pos(int x, int y) :_x(x) , _y(y) {} int _x; int _y; }; template<size_t M,size_t N> class Maze { public: Maze(int **arr, FILE* fp)//构造迷宫 :_row(M) , _col(N) , _maze(arr) { int ch = 0; assert(fp); for (size_t i = 0; i < _row; ++i)//从文件中读取迷宫 { for (size_t j = 0; j < _col;) { char ch = fgetc(fp); if (ch == '0' || ch == '1') { _maze[i][j] = ch - '0'; ++j; } } } fclose(fp); } bool PassMaze(Pos Entry) { //递归算法 Pos cur = Entry; Pos next = cur; _maze[next._x][next._y] = 2;//将走过的路进行标记 //判断是否为出口 if (next._x == N - 1) { return true; } //向上查探 next._x -= 1; if (_CheckAccess(next)) { if (PassMaze(next)) { return true; } } //向右查探 next = cur; next._y += 1; if (_CheckAccess(next)) { if (PassMaze(next)) { return true; } } //向下查探 next = cur; next._x += 1; if (_CheckAccess(next)) { if (PassMaze(next)) { return true; } } //向左查探 next = cur; next._y -= 1; if (_CheckAccess(next)) { if (PassMaze(next)) { return true; } } //走到死胡同时标记为3 _maze[cur._x][cur._y] = 3; return false; } ~Maze() { for (int i = 0; i <_row; ++i) { delete[] _maze[i]; } delete[] _maze; } friend ostream& operator<<(ostream& out, Maze& m); protected: bool _CheckAccess(Pos cur) { if (cur._x >= 0 && cur._x < N && cur._y >= 0 && cur._y < N && _maze[cur._x][cur._y] == 1) { return true; } return false; } private: int** _maze; int _row; int _col; }; ostream& operator<<(ostream& out, Maze<10,10>& m) { for (int i = 0; i < m._row; ++i) { for (int j = 0; j < m._col; ++j) { out << m._maze[i][j] << " "; } cout << endl; } return out; }

测试代码:

void Test() { FILE* fp = fopen("Maze.txt", "r"); int row = 0; int col = 0; fscanf(fp, "%d %d", &row, &col); int Start_x = 0; int Start_y = 0; fscanf(fp, "%d %d", &Start_x, &Start_y); //动态开辟二维数组 int **arr = new int*[row]; for (int i = 0; i < col; ++i) { arr[i] = new int[col]; } Maze<10,10> maze(arr,fp);//初始化迷宫 cout << maze << endl; cout << maze.PassMaze(Pos(Start_x, Start_y)) << endl; cout << maze << endl; fclose(fp); } int main() { Test(); return 0; }

2.非递归走迷宫(利用栈来保存迷宫的路径)

#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; #include<cassert> #include<stack> struct Pos//设置坐标 { Pos(int x, int y) :_x(x) , _y(y) {} int _x; int _y; }; template<size_t M,size_t N> class Maze { public: Maze(int **arr, FILE* fp)//构造迷宫 :_row(M) , _col(N) , _maze(arr) { int ch = 0; assert(fp); for (size_t i = 0; i < _row; ++i)//从文件中读取迷宫 { for (size_t j = 0; j < _col;) { char ch = fgetc(fp); if (ch == '0' || ch == '1') { _maze[i][j] = ch - '0'; ++j; } } } fclose(fp); } //使用循环 bool PassMaze(Pos Entry) { stack<Pos > s; s.push(Entry); _maze[Entry._x][Entry._y] = 2; while (!s.empty()) { Pos cur = s.top(); _maze[cur._x][cur._y] = 2; if (cur._x == N - 1) { return true; } //向上查探 Pos next = cur; next._x -= 1; if (_CheckAccess(next)) { s.push(next); continue; } //向右查探 next = cur; next._y += 1; if (_CheckAccess(next)) { s.push(next); continue; } //向下查探 next = cur; next._x += 1; if (_CheckAccess(next)) { s.push(next); continue; } //向左查探 next = cur; next._y -= 1; if (_CheckAccess(next)) { s.push(next); continue; } s.pop(); _maze[cur._x][cur._y] = 3; } return false; } ~Maze() { for (int i = 0; i <_row; ++i) { delete[] _maze[i]; } delete[] _maze; } friend ostream& operator<<(ostream& out, Maze& m); protected: bool _CheckAccess(Pos cur) { if (cur._x >= 0 && cur._x < N && cur._y >= 0 && cur._y < N && _maze[cur._x][cur._y] == 1) { return true; } return false; } private: int** _maze; int _row; int _col; }; ostream& operator<<(ostream& out, Maze<10,10>& m) { for (int i = 0; i < m._row; ++i) { for (int j = 0; j < m._col; ++j) { out << m._maze[i][j] << " "; } cout << endl; } return out; }

测试代码如下:

void Test() { FILE* fp = fopen("Maze.txt", "r"); int row = 0; int col = 0; fscanf(fp, "%d %d", &row, &col); int Start_x = 0; int Start_y = 0; fscanf(fp, "%d %d", &Start_x, &Start_y); //动态开辟二维数组 int **arr = new int*[row]; for (int i = 0; i < col; ++i) { arr[i] = new int[col]; } Maze<10,10> maze(arr,fp);//初始化迷宫 cout << maze << endl; cout << maze.PassMaze(Pos(Start_x, Start_y)) << endl; cout << maze << endl; fclose(fp); } int main() { Test(); return 0; }

迷宫中有多条路径时,查找最短路径:

第一种方法:(有缺陷)

先走一条路,走到出口时不返回而是将出口封住,从而利用回溯查找第二条路,这种方法只有在特定情况下(当每条路径的出口都不相同时)才会成功,而当遇到下面的情况(有多条路径是一个出口时),就有可能找不到最优路径。

迷宫(0代表墙,1代表通路,2代表入口):

0020000000 0011100000 0000100000 1000100000 0000011110 0011100000 0010000000 0011000000 0001111000 0000001000

方法二、(有缺陷)

利用递归,当找到一条路时,并不进行返回,即就是获取迷宫路径的函数没有返回值, 当找到一条路径时,会进行回溯至下一条路径,直到最终走完迷宫中所有的通路。

这里需要借助两个栈,一个path栈保存每次走的路径,另一个shortpath保存最短路径。

缺陷:标记方式的问题:走过的通路标记为2,而不通的路标记为3. 那么走过的路径就不会再走,那么当有几条路径的出口相同时,查找的最优解就可能不是我们想要的结果。

方法三、(最终可以适用任何场景)

和上述方式大致相同,只是改变了标记方式:使得走过的路径还可以再走。 将入口设置为2,每走一步将此位置的值设置为上个位置值加1. 利用某种条件,就可以找到所有的路径。

求迷宫的最优解需要注意的问题是: 1.必须保证所有的路径都可以走一遍;(使用递归,找到一条路径并不返回) 2.必须保证走过的路径还可以再走(该标记方式)

代码如下:

#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; #include<cassert> #include<stack> struct Pos//设置坐标 { Pos(int x, int y) :_x(x) , _y(y) {} int _x; int _y; }; template<size_t M,size_t N> class Maze { public: Maze(int **arr, FILE* fp)//构造迷宫 :_row(M) , _col(N) , _maze(arr) { int ch = 0; assert(fp); for (size_t i = 0; i < _row; ++i)//从文件中读取迷宫 { for (size_t j = 0; j < _col;) { char ch = fgetc(fp); if (ch == '0' || ch == '1' || ch == '2') { _maze[i][j] = ch - '0'; ++j; } } } fclose(fp); } //递归算法走迷宫 bool PassMaze(Pos Entry) { Pos cur = Entry; Pos next = cur; _maze[next._x][next._y] = 2;//将走过的路进行标记 //判断是否为出口 if (next._x == N - 1) { return true; } //向上查探 next._x -= 1; if (_CheckAccess(next)) { if (PassMaze(next)) { return true; } } //向右查探 next = cur; next._y += 1; if (_CheckAccess(next)) { if (PassMaze(next)) { return true; } } //向下查探 next = cur; next._x += 1; if (_CheckAccess(next)) { if (PassMaze(next)) { return true; } } //向左查探 next = cur; next._y -= 1; if (_CheckAccess(next)) { if (PassMaze(next)) { return true; } } //走到死胡同时标记为3 _maze[cur._x][cur._y] = 3; return false; } //获取迷宫的最优解 void GetShortPath(Pos Entry, stack<Pos>& path, stack<Pos>& shortPath) { //递归算法 Pos cur = Entry; Pos next = cur; path.push(cur); //_maze[next._x][next._y] = 2;//将走过的路进行标记 //判断是否为出口 if (next._x == N - 1) { Print(); if (shortPath.empty() || path.size() < shortPath.size()) { shortPath = path; } } //向上查探 next._x -= 1; if (_CheckAccess(next, cur)) { _maze[next._x][next._y] = _maze[cur._x][cur._y] + 1; GetShortPath(next, path, shortPath); } //向右查探 next = cur; next._y += 1; if (_CheckAccess(next, cur)) { _maze[next._x][next._y] = _maze[cur._x][cur._y] + 1; GetShortPath(next, path, shortPath); } //向下查探 next = cur; next._x += 1; if (_CheckAccess(next,cur)) { _maze[next._x][next._y] = _maze[cur._x][cur._y] + 1; GetShortPath(next, path, shortPath); } //向左查探 next = cur; next._y -= 1; if (_CheckAccess(next,cur)) { _maze[next._x][next._y] = _maze[cur._x][cur._y] + 1; GetShortPath(next, path, shortPath); } path.pop(); } ~Maze() { for (int i = 0; i <_row; ++i) { delete[] _maze[i]; } delete[] _maze; } //friend ostream& operator<<(ostream& out, Maze& m); void Print() { if (_maze) { for (size_t i = 0; i < N; ++i) { for (size_t j = 0; j < N; ++j) { cout << _maze[i][j] << " "; } cout << endl; } cout << endl; } } protected: bool _CheckAccess(Pos cur) { if (cur._x >= 0 && cur._x < N && cur._y >= 0 && cur._y < N && _maze[cur._x][cur._y] == 1) { return true; } return false; } bool _CheckAccess(Pos& next,Pos& cur) { if (next._x >= 0 && next._x < N && next._y >= 0 && next._y < N ) { if (_maze[next._x][next._y] == 1) { return true; } else if (_maze[next._x][next._y] == 0) { return false; } else { if (_maze[cur._x][cur._y] + 1 < _maze[next._x][next._y]) { return true; } } } return false; } private: int** _maze; int _row; int _col; }; void Test() { FILE* fp = fopen("Maze.txt", "r"); int row = 0; int col = 0; fscanf(fp, "%d %d", &row, &col); int Start_x = 0; int Start_y = 0; fscanf(fp, "%d %d", &Start_x, &Start_y); //动态开辟二维数组 int **arr = new int*[row]; for (int i = 0; i < col; ++i) { arr[i] = new int[col]; } Maze<10,10> maze(arr,fp);//初始化迷宫 maze.Print(); //cout << maze << endl; stack<Pos > path, shortPath; maze.GetShortPath(Pos(Start_x, Start_y),path,shortPath); //maze.Print(); fclose(fp); } int main() { Test(); return 0; }

结果显示(打印的太难看,表示一下意思就好,笑脸):

转载请注明原文地址: https://www.6miu.com/read-67407.html

最新回复(0)