学霸的迷宫 经典BFS

2 篇文章 0 订阅
1 篇文章 0 订阅
问题描述
  学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。
输入格式
  第一行两个整数n, m,为迷宫的长宽。
  接下来n行,每行m个数,数之间没有间隔,为0或1中的一个。0表示这个格子可以通过,1表示不可以。假设你现在已经在迷宫坐标(1,1)的地方,即左上角,迷宫的出口在(n,m)。每次移动时只能向上下左右4个方向移动到另外一个可以通过的格子里,每次移动算一步。数据保证(1,1),(n,m)可以通过。
输出格式
  第一行一个数为需要的最少步数K。
  第二行K个字符,每个字符∈{U,D,L,R},分别表示上下左右。如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。
样例输入
Input Sample 1:
3 3
001
100
110

Input Sample 2:
3 3
000
000
000
样例输出
Output Sample 1:
4
RDRD

Output Sample 2:
4
DDRR
数据规模和约定
  有20%的数据满足:1<=n,m<=10
  有50%的数据满足:1<=n,m<=50

  有100%的数据满足:1<=n,m<=500


先用BFS得到最小路径矩阵,再用DFS以迷宫终点作为搜索起点(可以保证一定能得到完整路径),输出字典序最小的最短路径

在输出字典序最小的路径,我是将U,D,L,R排成 U R L D 这种顺序,然后在DFS中按此顺序移动,得到的第一个序列经过倒置和U<->D,R<->L的变换就是字典序最小的序列

比如Sample 1   DFS中移动得到的第一个序列是0 2 0 2 ,由于这是从终点作搜索起点的,所以得倒置,得到2 0 2 0

并将方向反向,得到 1 3 1 3,转换成字母则是 R D R D

下面是代码

#include<cstring>
#include<string>
#include<stdio.h>
#include<iostream>
#include<queue>
#include<stack>
#include<vector>
#include<algorithm>
using namespace std;

#define MAX 505
char maze[MAX][MAX];
int  x[MAX][MAX];
int dr[] = { -1,0,0,1 };
int dc[] = { 0,1,-1,0 };
char dirs[] = { 'U','R','L','D' };
int r, c, minlen, numOfPath = 0;
string path;
string minPath;
vector<int> path_num;
class Cell
{
public:
	int x;
	int y;
	int dir;
	Cell() :x(x), y(y) {}
	Cell(int x, int y) :x(x), y(y) {}
};
Cell walk(const Cell &c, int dir)
{
	return Cell(c.x + dr[dir], c.y + dc[dir]);
}
bool is_inside(const Cell &cell)
{
	return cell.x >= 0 && cell.x < r&&cell.y >= 0 && cell.y < c;
}
bool is_walk(const Cell &cell)
{
	return maze[cell.x][cell.y] == '0'&&x[cell.x][cell.y] == -1;
}
bool is_arrive(const Cell &cell)
{
	return cell.x == r - 1 && cell.y == c - 1;
}
void trans(vector<int> &s, string &str)
{
	int size = s.size();
	reverse(s.begin(), s.end());
	for (int i = 0;i != size;++i)
	{
		s[i] = 3 - s[i];
		str.push_back(dirs[s[i]]);
	}

}
void print_path()
{
	int len = minlen;
	stack<Cell> s;
	s.push(Cell(r - 1, c - 1));
	while (!s.empty())
	{
		Cell cur = s.top();
		if (cur.x != r - 1 || cur.y != c - 1)
			path_num.push_back(cur.dir);
		if (cur.x == 0 && cur.y == 0)
		{
			trans(path_num, path);
			return;
		}
		s.pop();
		for (int i = 0;i != 4;++i)
		{
			Cell next = walk(cur, i);
			if (x[next.x][next.y] == len - 1)
			{
				next.dir = i;
				s.push(next);
			}
		}
		--len;
	}
}

void bfs()
{
	queue<Cell> q;
	q.push(Cell(0, 0));
	x[0][0] = 0;
	while (!q.empty())
	{
		Cell cur = q.front();
		if (is_arrive(cur))
		{
			minlen = x[cur.x][cur.y];
			return;
		}
		q.pop();
		for (int i = 0;i != 4;++i)
		{
			Cell next = walk(cur, i);
			if (is_inside(next) && is_walk(next))
			{
				x[next.x][next.y] = x[cur.x][cur.y] + 1;
				q.push(next);
			}
		}
	}
}
int main()
{
	/*scanf("%d%d", &r, &c);*/
	cin >> r >> c;
	memset(maze, '1', sizeof(maze));
	memset(x, -1, sizeof(x));
	for (int i = 0;i != r;++i)
	{
		cin >> maze[i];
		/*scanf("%s", maze[i]);*/
	}
	bfs();
	printf("%d\n", minlen);
	print_path();
	cout << path << "\n";
	return 0;
}

在这里DFS我用的是非递归,个人觉得他的非递归思路更清晰,而且不会担心栈溢出,速度也会更快一些。很多题目必须用非递归,不然就会超时。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值