迷宫最优解问题:
迷宫地形我们可以通过读文件的形式,通过已知入口逐个遍历坐标寻找通路。
文件如图:
每个坐标的位置用结构体来记录:
- struct Pos //位置坐标
- {
- int _row;
- int _col;
- };
定义行列范围
- #define M 10 //行
- #define N 10 //列
初始化迷宫数组:
将通过读文件的方式获取的字符转成整型数据,保存在M行N列的数组中。
- void InitMaze(int* maze)
- {
- struct WavHeadhWAV;
- FILE* fout = fopen("MazeMap.txt", "r"); // .txt文件要放在当前目录下
- if (fout == NULL) // 这里一定要检查读文件是否成功
- {
- cout << "can't find MazeMap.txt !" << endl<<endl;
- return;
- }
- for (int i = 0; i < M; i++)
- {
- for (int j = 0; j < N;)
- {
- char ch = fgetc(fout);
- if (ch=='1'||ch=='0')
- {
- maze[i*N + j] = ch - '0';
- j++;
- }
- }
- }
- fclose(fout);
- }
回溯查找通路:
利用栈来存储通路,通过上下左右四个方向依次遍历,如果该位置满足条件,就将它入栈,并将该位置的数据置为2;如果四个方向都不满足条件就执行出栈操作,回溯查找满足条件的位置(这时候如果栈为空了,说明查找通路失败),继续循环遍历,直到找到出口为止。
这里建立一个最小栈,如果找到出路,就将该栈赋给最小栈,并将出口置为1,继续回溯查找通路,如果又一次找到通路,就将该栈的长度与最小栈进行比较,如果该栈长度比最小栈还要小,就将它再一次赋给最小栈(保证最小栈是最短的通路),继续回溯,直到整个栈为空,回到了入口,程序结束。
- bool SearchMazePath(int *maze, Pos entry, stack<Pos>& paths) // 利用栈回溯查找通路,并实现迷宫的最优解
- {
- assert(maze);
- stack<Pos>min;
- paths.push(entry);
- while (!paths.empty())
- {
- Pos cur = paths.top();
- maze[cur._row*N+cur._col] = 2;
- if (cur._row==M-1)
- {
- if (paths.size()< min.size() || min.size() == 0)
- {
- min = paths;
- }
- paths.pop();
- maze[min.top()._row*N + min.top()._col] = 1;
- }
- Pos next = cur;
- next._col--; //左
- if (CheckIsAccess(maze, next))
- {
- paths.push(next);
- maze[next._row*N + next._col] = 2;
- continue;
- }
- next = cur;
- next._col++; //右
- if (CheckIsAccess(maze, next))
- {
- paths.push(next);
- maze[next._row*N + next._col] = 2;
- continue;
- }
- next = cur;
- next._row--; //上
- if (CheckIsAccess(maze, next))
- {
- paths.push(next);
- maze[next._row*N + next._col] = 2;
- continue;
- }
- next = cur;
- next._row++; //下
- if (CheckIsAccess(maze, next))
- {
- paths.push(next);
- maze[next._row*N + next._col] = 2;
- continue;
- }
- paths.pop();
- }
- if (paths.empty()&&!min.empty())
- {
- maze[min.top()._row*N + min.top()._col] = 2;
- return true;
- }
- return false;
- }
检查该位置是否合法:(坐标在行列范围之内)
- bool CheckIsAccess(int* maze, const Pos& next) // 检查该位置是否合法
- {
- assert(maze);
- if ((next._row >= 0 && next._row <= N) && (next._col >= 0 && next._col < M) && maze[next._row*N + next._col] == 0)
- {
- return true;
- }
- return false;
- }
打印迷宫:
- void PrintMaze(int *maze) // 打印迷宫
- {
- assert(maze);
- for (int i = 0; i < M; i++)
- {
- for (int j = 0; j < N; j++)
- {
- cout << maze[i*N + j] <<" " ;
- }
- cout << endl;
- }
- cout << endl;
- }
下面简单阐述下使用栈(循环)和使用递归来走迷宫的区别:
使用递归:
分为两个部分:
一部分为已走过的路径(通路)
另一问题是:递归的子问题(根据下一个可以通的位置继续查找出口)。
将走过的路径保存在栈帧内。
递归结束时返回上一层栈帧,销毁当前栈帧。使用栈(保存走过的路径)
分为两个部分:
一部分为已走过的路径(通路),将其保存在栈内
另一部分为:
试探四个方向,沿着某个可以走通的方向一直试探下去,直到走不通时,退出栈顶元素,栈顶元素始终为当前所处的位置。
#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;
}