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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章