BFS广度优先遍历搜索最短路径(迷宫问题)

最近笔试经常遇到路径搜索的问题,可是每一次都不知道如何去写,决定整理一下。
其实都是用bfs解决的。最简单的迷宫类问题就是输入一个m*n的矩阵,其中0代表可走的路,1代表障碍物,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。

先来看第一题

题目描述:
假设以一个nm的矩阵作为棋盘,每个棋位对应一个二维座标 (x, y)。你有一颗棋子位于左上起点(0, 0),现在需要将其移动到右下底角 (n-1, m-1),棋子可以向相邻的上下左右位置移动,每个座标最多只能经过一次。棋盘中散布着若干障碍,障碍物不能跨越,只能绕行,问是否存在到达右下底角的路线?若存在路线,输出所需的最少移动次数;若不存在,输出0。 Input 第一行三个正整数n,m和k,代表棋盘大小与障碍物个数 1< n、m < 100, k < nm 第二行至第k+1行,每行为两个整数x和y,代表k个障碍物的座标。
输入
输入三个正整数n,m和k,代表棋盘大小与障碍物个数 1< n、m < 100, k < n*m
第二行至第k+1行,每行为两个整数x和y,代表k个障碍物的座标。
输出
输出从起点到终点的最短路径的长度,如果不存在,即输出0
样例输入
5 10 20
1 4
0 3
2 2
4 4
1 7
1 3
2 3
1 8
3 7
3 5
1 5
3 9
4 8
4 0
4 1
2 1
0 7
2 4
4 5
0 8
样例输出
0
思路
这道题没有直接让输矩阵,而是告诉了障碍物的下标,那么首先我们要转换为最基本的问题,就是把有障碍物的位置用1表示,其余用0表示。
代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>

using namespace std;

int ab[4][2] = { { 0,1},{0,-1},{1,0},{-1,0 } };
struct node {
	int hx, hy, step;
};
struct father
{
	int x;
	int y;
};

queue<node> q;//队列
node start;//入口
node endd;//出口
node rec[20][20];//记录最短路径

int bfs(vector<vector<int>> maze, int m, int n) {
	vector<int> v(n);
	vector<vector<int>> vis(m, v);//标记每一点是否被访问过	

	//设置起点和终点的座标
	start.hx = 0; start.hy = 0; start.step = 0;
	endd.hx = m - 1; endd.hy = n - 1;
	q.push(start);
	vis[start.hx][start.hy] = 1;
	while (!q.empty())
	{
		node s = q.front();
		q.pop();
		for (int i = 0; i < 4; i++) {
			node tmp = s;
			tmp.step++;
			tmp.hx += ab[i][0];
			tmp.hy += ab[i][1];

			if (tmp.hx > n - 1 || tmp.hy > m - 1 || tmp.hx < 0 || tmp.hy < 0) continue;

			if (maze[tmp.hx][tmp.hy] == 1) continue;

			if (vis[tmp.hx][tmp.hy] != 1)
			{
				q.push(tmp);
				vis[tmp.hx][tmp.hy] = 1;
				rec[tmp.hx][tmp.hy].hx = s.hx;
				rec[tmp.hx][tmp.hy].hy = s.hy;
			}
			if (tmp.hx == endd.hx&&tmp.hy == endd.hy)
			{
				return tmp.step;
			}
				
		}
	}
	return -1;
}

void dfs(int x, int y)
{

	if (x == 0 && y == 0) return;//找到父节点是起点的格子了
	else
		dfs(rec[x][y].hx, rec[x][y].hy);
	cout << rec[x][y].hx << " " << rec[x][y].hy << endl;

}

int main()
{
	int k;//障碍物个数
	int m, n;//迷宫的行和列
	cin >> n >> m >> k;
	vector<int> ma(n);
	vector<vector<int>> maze(m, ma);//迷宫
	
	vector<int> obp(2);
	vector<vector<int>> ob(k,obp);
	for (int i = 0; i < k; i++)
	{
		for (int j = 0; j < 2; j++)
		{
			cin >> ob[i][j];
		}
	}

	for(int i=0;i<k;i++)
	{			
		maze[ob[i][0]][ob[i][1]] = 1;
	}
	/*for (int i = 0; i < m; i++)
	{
		for (int j = 0; j < n; j++)
		{
			cout << maze[i][j]<<" ";//输出构造好的迷宫
		}		
		cout << endl;
	}*/
	int ans = bfs(maze, m, n);
	if (ans < 0) 
		cout << 0 << endl;
	else
	{
		cout << ans << endl;
		dfs(n-1, m-1);//从终点开始找他的父节点
	}
	cout << m-1 << " " << n-1 << endl;

 	system("pause");
	return 0;
}

第二题

题目描述
薯队长最近在玩一个迷宫探索类游戏,迷宫是一个N*N的矩阵形状,其中会有一些障碍物禁止通过。这个迷宫还有一个特殊的设计,它的左右边界以及上下边界是连通的,比如在(2,n)的位置继续往右走一格可以到(2,1), 在(1,2)的位置继续往上走一格可以到(n,2)。请问薯队长从起点位置S,最少走多少格才能到达迷宫的出口位置E。
输入
第一行正整数 N。 接下来 N 行 字符串。
’.’表示可以通过,’#’表示障碍物, ’S’表示起点(有且仅有一个),’E’表示出口(有且仅有一个)。
对于50%的数据
0 < N < 10
对于100%的数据
0 < N < 10^3
输出
输出一个整数。表示从S到E最短路径的长度, 无法到达则输出 -1
样例输入
5
.#…
…#S.
.E###
……
……
样例输出
4
提示
向右来到(2,5)
向右来到(2,1)
向下来到(3,1)
向右来到(3,2)
思路
和第一道题不一样的是起点和终点不再是迷宫的左上角和右下角了。很简单,遍历迷宫,找到起点和终点座标就好了。其实这道题有一个难点就是迷宫的它的左右边界以及上下边界是连通的。暂时没时间想,就先放一个不连通的代码吧。
代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>

using namespace std;

int ab[4][2] = { { 0,1},{0,-1},{1,0},{-1,0 } };
struct node {
	int hx, hy, step;
};
struct father
{
	int x;
	int y;
};

queue<node> q;//队列
node start;//入口
node endd;//出口
int n;
vector<string> maze;//迷宫
node rec[20][20];//记录最短路径

int bfs() {
	vector<int> v(n);
	vector<vector<int>> vis(n, v);//标记每一点是否被访问过

	q.push(start);
	vis[start.hx][start.hy] = 1;
	while (!q.empty())
	{
		node s = q.front();
		q.pop();
		for (int i = 0; i < 4; i++) {
			node tmp = s;
			tmp.step++;
			tmp.hx += ab[i][0];
			tmp.hy += ab[i][1];

			if (tmp.hx > n - 1 || tmp.hy > n - 1 || tmp.hx < 0 || tmp.hy < 0) continue;//超出边界

			if (maze[tmp.hx][tmp.hy] == '#') continue;//遇到障碍物

			if (vis[tmp.hx][tmp.hy] != 1)
			{
				q.push(tmp);
				vis[tmp.hx][tmp.hy] = 1;
				rec[tmp.hx][tmp.hy].hx = s.hx;
				rec[tmp.hx][tmp.hy].hy = s.hy;
			}
			if (tmp.hx == endd.hx&&tmp.hy == endd.hy)
			{
				return tmp.step;
			}

		}
	}
	return -1;
}

void dfs(int x, int y)
{
	if (x == start.hx && y == start.hy) return;//找到父节点是起点的格子了
	else
		dfs(rec[x][y].hx, rec[x][y].hy);
	cout << rec[x][y].hx << " " << rec[x][y].hy << endl;
}

int main() {
	
	cin >> n;
	
	for (int i = 0; i < n; i++)
	{
		string str;
		cin >> str;
		maze.push_back(str);
	}

	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			if (maze[i][j] == 'S')
				start.hx = i, start.hy = j;//将开始点存储到start中
			if (maze[i][j] == 'E')
				endd.hx = i, endd.hy = j;//将终点存储到endd中
		}
	}
	int res = bfs();
	cout << res << endl;
	if (res > 0)
	{
		dfs(endd.hx, endd.hy);
	}
	system("pause");
	return 0;
}

推箱子

题目描述
大家一定玩过“推箱子”这个经典的游戏。具体规则就是在一个NM的地图上,有1个玩家、1个箱子、1个目的地以及若干障碍,其余是空地。玩家可以往上下左右4个方向移动,但是不能移动出地图或者移动到障碍里去。如果往这个方向移动推到了箱子,箱子也会按这个方向移动一格,当然,箱子也不能被推出地图或推到障碍里。当箱子被推到目的地以后,游戏目标达成。现在告诉你游戏开始是初始的地图布局,请你求出玩家最少需要移动多少步才能够将游戏目标达成。
输入描述:
每个测试输入包含1个测试用例
第一行输入两个数字N,M表示地图的大小。其中0<N,M<=8。
接下来有N行,每行包含M个字符表示该行地图。其中 . 表示空地、X表示玩家、表示箱子、#表示障碍、@表示目的地。
每个地图必定包含1个玩家、1个箱子、1个目的地。
输出描述:
输出一个数字表示玩家最少需要移动多少步才能将游戏目标达成。当无论如何达成不了的时候,输出-1。
输入例子1:
4 4

@

.X…
6 6
…#…

#
##…
…##.#
…X…
.@#…
输出例子1:
3
11
思路

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>

using namespace std;

int ab[4][2] = { { 0,1},{0,-1},{1,0},{-1,0 } };//右左下上

struct node {
	int hx, hy, step;
	int bx, by;
};
queue<node> q;//队列
node start;//起点
int m, n;
vector<vector<char>> maze;//迷宫
int vis[8][8][8][8];

int bfs() {

	q.push(start);
	while (!q.empty())
	{
		node s = q.front();
		q.pop();
		vis[s.hx][s.hy][s.bx][s.by] = 1;
		if (maze[s.bx][s.by] == '@') return s.step;//箱子成功推到目的地
		for (int i = 0; i < 4; i++) {
			node tmp = s;
			tmp.step++;
			tmp.hx += ab[i][0];
			tmp.hy += ab[i][1];

			if (tmp.hx > n - 1 || tmp.hy > m - 1 || tmp.hx < 0 || tmp.hy < 0) continue;//人物越界

			if (maze[tmp.hx][tmp.hy] == '#') continue;//人碰到障碍物

			if (tmp.hx == tmp.bx && tmp.hy == tmp.by)
				tmp.bx += ab[i][0], tmp.by += ab[i][1];

			if (tmp.bx > n - 1 || tmp.by > m - 1 || tmp.bx < 0 || tmp.by < 0) continue;//箱子越界

			if (maze[tmp.bx][tmp.by] == '#') continue;//箱子碰到障碍物

			if (vis[tmp.hx][tmp.hy][tmp.bx][tmp.by] != 1)
			{
				q.push(tmp);
			}
		}
	}
	return -1;
}

int main() {
	cin >> n >> m;
	
	for (int i = 0; i < n; i++)
	{
		vector<char> str;
		for (int j = 0; j < m; j++)
		{
			char ch;
			cin >> ch;			
			str.push_back(ch);
		}
		maze.push_back(str);
	}

	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			if (maze[i][j] == 'X')
				start.hx = i, start.hy = j;//记录玩家的起点位置

			if (maze[i][j] == '*')
				start.bx = i, start.by = j;//记录box的位置
		}
	}

	int res = bfs();
	cout << res << endl;
	
	system("pause");
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章