搜索:maze
题目链接:https://ac.nowcoder.com/acm/problem/15665
题目描述
小明来到一个由n x m个格子组成的迷宫,有些格子是陷阱,用'#'表示,小明进入陷阱就会死亡,'.'表示没有陷阱。小明所在的位置用'S'表示,目的地用'T'表示。
小明只能向上下左右相邻的格子移动,每移动一次花费1秒。
有q个单向传送阵,每个传送阵各有一个入口和一个出口,入口和出口都在迷宫的格子里,当走到或被*传送到一个有传送阵入口的格子时,小明可以选择是否开启传送阵。如果开启传送阵,小明就会被传送到出口对应的格子里,这个过程会花费3秒;如果不开启传送阵,将不会发生任何事情,小明可以继续向上下左右四个方向移动。
一个格子可能既有多个入口,又有多个出口,小明可以选择任意一个入口开启传送阵。使用传送阵是非常危险的,因为有的传送阵的出口在陷阱里,如果小明使用这样的传送阵,那他就会死亡。也有一些传送阵的入口在陷阱里,这样的传送阵是没有用的,因为小明不能活着进入。请告诉小明活着到达目的地的最短时间。
输入描述
有多组数据。对于每组数据:
第一行有三个整数n,m,q(2≤ n,m≤300,0≤ q ≤ 1000)。
接下来是一个n行m列的矩阵,表示迷宫。
最后q行,每行四个整数x1,y1,x2,y2(0≤ x1,x2< n,0≤ y1,y2< m),表示一个传送阵的入口在x1行y1列,出口在x2行y2列。
输出描述
如果小明能够活着到达目的地,则输出最短时间,否则输出-1。
示例
输入
5 5 1
..S..
.....
.###.
.....
..T..
1 2 3 3
5 5 1
..S..
.....
.###.
.....
..T..
3 3 1 2
5 5 1
S.#..
..#..
###..
.....
....T
0 1 0 2
4 4 2
S#.T
.#.#
.#.#
.#.#
0 0 0 3
2 0 2 2
输出
6
8
-1
3
分析
本题是普通迷宫问题的变种,但仍是在求迷宫中某点是否可达,以及其路径长度(本题为耗费时间),因此我们的主要思路依然为广搜。
回忆一下,通常我们广搜就是按照远近,从近到远挨着搜,本题中传送阵的出现虽然有所变化,但仅仅是扩充了试探的原则,不仅仅是"挨着搜",增加了传送阵搜,其余不变,因此为了保持由近及远的原则,我们可以使用优先队列,出队顺序由耗时决定。
以上即为本题主要思路,还有一些细节均在代码注释中不再赘述。
AC代码
#include <iostream>
#include <vector>
#include <queue>
#include <map>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
struct pos
{
int x,y;
int time;
pos (int X=0,int Y=0,int Time=0)
{
x=X;
y=Y;
time=Time;
}
bool operator < (const pos &a) const
{
return this->time >= a.time; //重载要注意
}
}; //注意,优先队列内部排序时虽然利用小于号,但却是大的在前,我们这要求小的在前
int n,m,q;
int t,s;
bool visited[300][300];
int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
bool mp[300][300]; //记录地图
int dis[300][300]; //记录从起点到各点所需时间
priority_queue<pos> Q; //优先队列 排序标准为到达该点的时间
map<int,vector<int>> z; //存储传送阵的数据 每一个int数表示坐标(行编号*列数+列编号)
//因为某个点可能是多个传送阵的起点,故此处使用vector
int bfs()
{
while(!Q.empty())
{
int x=Q.top().x;
int y=Q.top().y;
int time=Q.top().time;
if(x*m+y==t) return time; //注意出队时一个点的最短时间才被确定 因此终点判断放在这
Q.pop();
if(visited[x][y]) continue; //由于传送阵的存在某个点可能在队列中不止一个 先出来的时间最短
dis[x][y]=time; //并将之记录
visited[x][y]=true; //出队后才标记该点数据已定
//首先试探邻近点
for(int i=0;i<4;i++)
{
int newx=x+dir[i][0];
int newy=y+dir[i][1];
if(newx<0||newy<0||newx>=n||newy>=m)
continue;
if(visited[newx][newy])
continue;
if(!mp[newx][newy])
continue;
Q.push(pos(newx,newy,dis[x][y]+1));
}
//再试探传送
for(int i=0;i<z[x*m+y].size();i++)
{
int newx=z[x*m+y][i]/m;
int newy=z[x*m+y][i]%m;
if(!mp[newx][newy])
continue;
if(visited[newx][newy])
continue;
Q.push(pos(newx,newy,dis[x][y]+3));
}
}
return -1;
}
int main()
{
while(cin >> n >> m >> q)
{
cin.get();
memset(visited,0,sizeof(visited));
z.clear();
while(!Q.empty()) Q.pop();
for(int i=0;i<n;i++)
{
string temp;
getline(cin,temp);
for(int j=0;j<m;j++)
{
if(temp[j]=='#')
{
mp[i][j]=false;
continue;
}
mp[i][j]=true;
if(temp[j]=='S')
{
Q.push(pos(i,j,0)); //起点入队
s=i*m+j;
}
else if(temp[j]=='T') t=i*m+j;
}
}
for(int i=0;i<q;i++) //存储传送阵的数据
{
int x1,y1,x2,y2;
cin >>x1>>y1>>x2>>y2;
z[x1*m+y1].push_back(x2*m+y2);
}
cout << bfs() << endl;
}
return 0;
}