迷宫问题

代码链接:github

迷宫

如下图所示,在一个矩阵里面,0代表通路,1代表墙。 image image构成一个简单的迷宫


问题:

  • 1.那么怎样用程序来寻找迷宫的出口呢?
  • 2.如果有多个出口,如何寻找一条最短通路?
  • 3.如果迷宫里面存在环路,那么又如何寻找最短通路?

找通路之前

解决问题1

思路

首先需要得到这个迷宫,可以是程序运行时从文件中读取,也可以通过数组把迷宫保存起来。然后自定义一个类型存放可以通的结点坐标。找通路的的思想主要是下面三步:

1.试探

使用两个结点,一个结点表示当前位置,另一个结点表示下一步位置,并按一定的顺序(如顺时针)从起点开始试探下一个相邻结点是否可以通,若发现下一个结点是0,则把当前结点走到下一个位置,并把走过的结点放到栈里,方便回溯。

2.标记

走一步,先把上一步节点标记成2,表示已经走过了,防止试探时发生死循环,直到出口结束,或者走到死胡同,走到死胡同则回溯。

3.回溯

走到死胡同后,可以取出栈顶元素赋给当前位置,等于是走到死胡同,再往回走,看看以前走过的岔路口,还有没有没有试探过的路口,如果有继续试探,若没有继续出栈回溯,直到栈为空,表示走到入口,说明没有通路。把走过的回头路标记为3.

主要代码
bool CheckIsAccess(Pos pos)
	{
		if (pos._col >= 0 && pos._col<10&&pos._row<10&&\
			pos._row >= 0 && _maze[pos._row][pos._col] == 0)
		{
			return true;
		}
		return false;
	}

	//普通方法寻找通路
	bool GetPath(Pos entry)
	{
		stack<Pos> s;
		s.push(entry);
		Pos cur = entry;
		Pos next = entry;
		while (!s.empty())
		{
	
			if (cur._row == M-1)
			{
				_maze[cur._row][cur._col] = 2;
				printf("找到出口:[%d , %d]\n",cur._row, cur._col);
				//return true;
			}

			//上
			next = cur;
			next._row = cur._row - 1;
			if (CheckIsAccess(next))
			{
				_maze[cur._row][cur._col] = 2;
				s.push(cur);
				cur._row = next._row;
				continue;
			}
			
			//右
			next = cur;
			next._col = cur._col + 1;
			if (CheckIsAccess(next))
			{
				_maze[cur._row][cur._col] = 2;
				s.push(cur);
				cur._col = next._col;
				continue;
			}

			//下
			next = cur;
			next._row = cur._row + 1;
			if (CheckIsAccess(next))
			{
				_maze[cur._row][cur._col] = 2;
				s.push(cur);
				cur._row = next._row;
				continue;
			}

			//左
			next = cur;
			next._col = cur._col - 1;
			if (CheckIsAccess(next))
			{
				_maze[cur._row][cur._col] = 2;
				s.push(cur);
				cur._col= next._col;
				continue;
			}


			_maze[cur._row][cur._col] = 3;
			cur = s.top();
			s.pop();
		}
		return false;
	}

如何求最短路径

解决问题2

  • 利用栈求最短路径

首先在保存一个全局的栈和一个记录每次走的节点的栈(我叫他记录栈),开始试探寻找通路,并把走过的节点放入记录栈,找到其中一条通路,如果全局栈为空就把记录栈的数据赋给全局栈,若全局栈不为空,则比较两栈的大小,把全局栈替换为小的。然后回溯,继续寻找其他方向的通路,知道走回起点,即栈为空。

主要代码
//解决带环的试探下一个节点
	bool CheckIsAccess(Pos cur, Pos next)
	{
		if (next._col<N&&next._col>0 && next._row<M&&next._row>0\
			&& (_maze[next._row][next._col] == 0 || _maze[next._row][next._col] > _maze[cur._row][cur._col]))
		{
			return true;
		}
		return false;
	}
	//用栈寻找最短通路
	void StackGetShortPath(Pos entry, stack<Pos>& s, list<Pos>& shortpath)
	{
		s.push(entry);
		Pos cur = entry;
		Pos next = entry;
		while (!s.empty())
		{
			//进入入口开始试探下一个节点,若可以进入下一个节点,这说明没有到死胡同也没有到出口
			//上
			next = cur;
			next._row = cur._row - 1;
			if (CheckIsAccess(next))
			{
				_maze[cur._row][cur._col] = 2;
				s.push(cur);
				cur._row = next._row;
				continue;
			}

			//右
			next = cur;
			next._col = cur._col + 1;
			if (CheckIsAccess(next))
			{
				_maze[cur._row][cur._col] = 2;
				s.push(cur);
				cur._col = next._col;
				continue;
			}

			//下
			next = cur;
			next._row = cur._row + 1;
			if (CheckIsAccess(next))
			{
				_maze[cur._row][cur._col] = 2;
				s.push(cur);
				cur._row = next._row;
				continue;
			}

			//左
			next = cur;
			next._col = cur._col - 1;
			if (CheckIsAccess(next))
			{
				_maze[cur._row][cur._col] = 2;
				s.push(cur);
				cur._col = next._col;
				continue;
			}

			//上下左右都试探完,判断是否走到出口
			if (cur._row == M - 1 || cur._row == 0)
			{
				_maze[cur._row][cur._col] = 2;
				if (shortpath.empty() || s.size()< shortpath.size())
				{
					shortpath.clear();
					stack<Pos> tmp(s);
					while (!tmp.empty())
					{
						shortpath.push_front(tmp.top());
						tmp.pop();
					}
				}
			}
			//走到这说明走到死胡同,则进行回溯。
			_maze[cur._row][cur._col] = 3;
			cur = s.top();
			s.pop();
		}
	}
  • 递归求最短路径

利用递归也是利用一个栈来记录走过的节点,但是把递归的返回条件是发现四个方向都不能走时就返回(包括走到出口),最终可以把所有可以走的节点都遍历一遍,然后回到入口的位置。同样每次走到一个出口的时候用记录栈和全局栈比较,把全局栈来替换成小的通路。

解决问题3


带环问题如何求最短路径?如上图2

但是上面的求解最短路径的方法在遇到带环的迷宫时存在问题,因为带环的迷宫两条通路会共用一个出口(如上图2),也就是两条通路只能找到一次出口,所以记录栈只记录了其中一个通路,所以有可能找不到最短通路。要想把共用一个出口的两个通路都记录一遍,则有以下两种办法。

解决办法
  • 递归解决带环问题

同样按照上面找通路的方法寻找出口,不同的是改变标记的方法,用数字累加的方法记录走过的结点,试探的判断条件是下一个结点比当前位置的值大就可以走,(1是墙,防止把第二个位置累加到1,所以将入口改成2).看下面图理解如何实现的。

image

主要代码
//带环迷宫
	void GetShortPath(Pos entry, stack<Pos>& path, list<Pos>& shortpath)
	{
		Pos cur = entry;
		Pos next = cur;
		path.push(cur);

		//上
		next = cur;
		next._row -= 1;
		if (CheckIsAccess(cur, next))
		{
			_maze[next._row][next._col] = _maze[cur._row][cur._col] + 1;

			GetShortPath(next, path, shortpath);
		}

		//右
		next = cur;
		next._col += 1;
		if (CheckIsAccess(cur, next))
		{
			_maze[next._row][next._col] = _maze[cur._row][cur._col] + 1;
			GetShortPath(next, path, shortpath);
		}		
		//下
		next = cur;
		next._row += 1;
		if (CheckIsAccess(cur, next))
		{
			_maze[next._row][next._col] = _maze[cur._row][cur._col] + 1;
			GetShortPath(next, path, shortpath);
		}		
		//左
		next = cur;
		next._col -= 1;
		if (CheckIsAccess(cur, next))
		{
			_maze[next._row][next._col] = _maze[cur._row][cur._col] + 1;
			GetShortPath(next, path, shortpath);
		}
    	//上下左右都试探完,判断是否走到出口
		if (cur._row == M - 1||cur._row == 0)
		{
			if (shortpath.empty() || path.size()< shortpath.size())
			{
				shortpath.clear();
				stack<Pos> tmp(path);
				while (!tmp.empty())
				{
					shortpath.push_front(tmp.top());
					tmp.pop();
				}
			}
		}
    	//走到这说明走到死胡同,则进行回溯
		path.pop();

	}
  • 利用栈也可以解决

思想和递归的思想完全一样,只不过回溯的方式是利用pop栈顶元素来回溯。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
程序在VC++ 6下顺利编译通过。 一、 实验目的: (1) 熟练掌握链栈的基本操作及应用。 (2) 利用链表作为栈的存储结构,设计实现一个求解迷宫的非递归程序。 二、实验内容: 【问题描述】 以一个m×n的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。设计一个程序,对信任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。 【基本要求】 首先实现一个链表作存储结构的栈类型,然后编写一个求解迷宫的非递归程序。求得的通路以三元组(i,j,d)的形式输出,其中:(i,j)指示迷宫中的一个坐标,d表示走到下一坐标的方向。如:对于下列数据的迷宫,输出的一条通路为:(1,1,1),(1,2,2),(2,2,2),(3,2,3),(3,1,2),……。 【测试数据】 迷宫的测试数据如下:左上角(1,1)为入口,右下角(8,9)为出口。 1 2 3 4 5 6 7 8 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 0 1 0 1 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 1 1 1 0 0 1 1 1 0 0 0 1 0 1 1 1 0 0 0 0 0 0 以方阵形式输出迷宫及其通路。 输出: 请输入迷宫的长和宽:5 5 请输入迷宫内容: 0 1 1 0 0 0 0 1 1 0 1 0 0 1 1 1 0 0 1 0 1 1 0 0 0 迷宫的路径为 括号内的内容分别表示为(行坐标,列坐标,数字化方向,方向) (1,1,1,↓) (2,1,2,→) (2,2,1,↓) (3,2,1,↓) (4,2,2,→) (4,3,1,↓) (5,3,2,→) (5,4,2,→) (5,5,0,) 迷宫路径探索成功!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值