迷宫最优解问题

         最近看到一个比较有意思的题目,讲已知一个迷宫求最优解。这里就要提一下什么是迷宫了。

         

        如上图所示,其实迷宫就是一个二维数组,其中‘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;
}

     该算法的难点就在于标记方法,标记方法是能否成功解出这道题的关键。

     运行结果:

     图中红圈部分就是这样标记解决的例子,如果使用单一数字标记这道题是不会有正确结果的哦。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
广度先搜索算法可以用来求迷宫问题的最。该算法从起点开始,逐层扩展搜索,直到找到终点为止。在搜索过程中,需要记录每个节点的父节点,以便在找到终点后回溯路径。具体步骤如下: 1. 将起点加入队列,并标记为已访问。 2. 从队列中取出一个节点,遍历它的所有相邻节点。 3. 如果相邻节点未被访问过,将其加入队列,并标记为已访问。同时记录该节点的父节点为当前节点。 4. 如果相邻节点是终点,停止搜索。 5. 如果队列为空,说明没有找到终点,搜索失败。 6. 回溯路径,从终点开始,沿着每个节点的父节点一直回溯到起点,即可得到最。 下面是一个简单的 Python 代码实现: ```python def bfs(maze, start, end): queue = [start] visited = set([start]) parent = {start: None} while queue: node = queue.pop(0) if node == end: break for neighbor in get_neighbors(maze, node): if neighbor not in visited: queue.append(neighbor) visited.add(neighbor) parent[neighbor] = node if end not in parent: return None path = [] node = end while node: path.append(node) node = parent[node] path.reverse() return path def get_neighbors(maze, node): neighbors = [] for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]: x, y = node[0] + dx, node[1] + dy if 0 <= x < len(maze) and 0 <= y < len(maze[0]) and maze[x][y] == 0: neighbors.append((x, y)) return neighbors ``` 其中,maze 是一个二维数组,表示迷宫地图;start 和 end 分别是起点和终点的坐标。函数返回最的路径,如果找不到路径则返回 None。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值