最近看到一个比较有意思的题目,讲已知一个迷宫求最优解。这里就要提一下什么是迷宫了。
如上图所示,其实迷宫就是一个二维数组,其中‘1’代表墙,不能拖过,而'0'代表通路,是可以走的,给定一个入口和这个迷宫 求出最优(路径最短)的解。
求解大体算法是这样的 使用递归,从入口进入迷宫,从入口的上下左右四个方向探测,探测到可以走时,将来时的路做'标记 ',并将下一个点变成新的入口(递归子问题) 以此类推,便可以找到所有通路。至于最优解的算法,我们边看代码边讲。
struct Pos //一个结构体类,用于存储迷宫位置的行列
{
int _col;
int _row;
};
template<int M, int N>
class Maze
{
public:
Maze(int* maze)
{
int i, j;
for (i = 0; i < M; i++)
{
for (j = 0; j < N; j++)
_maze[i][j] = maze[i*N+j];
}
}
~Maze()
{}
void GetShortPath(Pos cur, stack<Pos>& path, stack<Pos>& shortpath)
{
//这是标记部分,这样标记的原因是为了防止回溯时将未走过的路堵死而导致本来可以通的路探测不到
if (path.empty())
{
path.push(cur);
_maze[cur._col][cur._row] = 2;
}
else
{
_maze[cur._col][cur._row] = _maze[path.top()._col][path.top()._row] + 1;
path.push(cur);
}
if (cur._col == M-1) //path和shortpath是两个栈结构,其中一个栈结构存储来时的路径,而另一个栈结构只有在通路找到并且是当前最短时才会更新
{
printf("找到一个出口a[%d][%d]\n", path.top()._col, path.top()._row);
if (shortpath.empty() || path.size() < shortpath.size())
{
shortpath = path;
path.pop();
return;
}
}
Pos next=cur;
//下为递归子问题的代码:
//up
next._col -= 1;
if (CheckAccess(cur, next))
{
GetShortPath(next, path, shortpath);
}
next = cur;
//right
next._row += 1;
if (CheckAccess(cur, next))
{
GetShortPath(next, path, shortpath);
}
next = cur;
//down
next._col += 1;
if (CheckAccess(cur, next))
{
GetShortPath(next, path, shortpath);
}
next = cur;
//left
next._row -= 1;
if (CheckAccess(cur, next))
{
GetShortPath(next, path, shortpath);
}
next = cur;
if (!path.empty())
path.pop();
return;
}
bool CheckAccess(Pos cur,Pos next)//探测是否可以通(0是通路,而比cur大的值也可以通是因为之前的通路探测过这个点才会导致这种情况,可画图思考)
{
if (cur._col >= 0 && cur._col < M&&cur._row >= 0 && cur._row < N)
{
if (_maze[next._col][next._row] == 0 || _maze[cur._col][cur._row] < _maze[next._col][next._row])
return true;
}
return false;
}
void ShowMaze()
{
int i, j;
for (i = 0; i < M; i++)
{
for (j = 0; j < N; j++)
cout << _maze[i][j] << ' ';
cout << endl;
}
}
protected:
int _maze[M][N];
};
以下为测试代码:
void TestMaze()
{
int a[10][10] = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 0, 0, 0, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 0, 1, 1, 1, 0, 1, 0, 1 },
{ 1, 1, 0, 1, 1, 1, 0, 0, 0, 1 },
{ 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
{ 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 0, 1, 1, 1 }
};
Maze<10, 10> maze((int*)a);
maze.ShowMaze();
stack<Pos> path, shortpath;
Pos entry = { 2, 0 };
maze.GetShortPath(entry, path, shortpath);
maze.ShowMaze();
cout << shortpath.size() << endl;
}
该算法的难点就在于标记方法,标记方法是能否成功解出这道题的关键。
运行结果:
图中红圈部分就是这样标记解决的例子,如果使用单一数字标记这道题是不会有正确结果的哦。