一、题目描述
Problem Description
Hogwarts正式开学以后,Harry发现在Hogwarts里,某些楼梯并不是静止不动的,相反,他们每隔一分钟就变动一次方向.
比如下面的例子里,一开始楼梯在竖直方向,一分钟以后它移动到了水平方向,再过一分钟它又回到了竖直方向.Harry发现对他来说很难找到能使得他最快到达目的地的路线,这时Ron(Harry最好的朋友)告诉Harry正好有一个魔法道具可以帮助他寻找这样的路线,而那个魔法道具上的咒语,正是由你纂写的.
Input
测试数据有多组,每组的表述如下:
第一行有两个数,M和N,接下来是一个M行N列的地图,’*’表示障碍物,’.’表示走廊,’|’或者’-‘表示一个楼梯,并且标明了它在一开始时所处的位置:’|’表示的楼梯在最开始是竖直方向,’-‘表示的楼梯在一开始是水平方向.地图中还有一个’S’是起点,’T’是目标,0<=M,N<=20,地图中不会出现两个相连的梯子.Harry每秒只能停留在’.’或’S’和’T’所标记的格子内.
Output
只有一行,包含一个数T,表示到达目标的最短时间.
注意:Harry只能每次走到相邻的格子而不能斜走,每移动一次恰好为一分钟,并且Harry登上楼梯并经过楼梯到达对面的整个过程只需要一分钟,Harry从来不在楼梯上停留.并且每次楼梯都恰好在Harry移动完毕以后才改变方向.
Sample Input
5 5
**..T
**.*.
..|..
.*.*.
S....
Sample Output
7
样例地图如下:
二、设计思路
本题目是经典使用BFS求迷宫最短路径的问题,首先创建一个结构体来分别存储每个坐标点和相应点对应的步数,再创建有一个方向二维数组来控制进行上、下、左、右移动。因为题目中还有一个小小的变化即里面的一个或多个坐标点会随着时间的变化让你只能上下这样走或者左右这样走,我们就得记录这样的梯子状态,规则如下:
如果一开始是竖的,即’|’,那么奇数次后就是横的,偶数次后还是原来的状态,横着的话同理。
所以我们记录一个数组jump[][],第一个里面放’|’OR’-’,第二个里面放1或者0。例如,一开始是’|’,我们到第三步还是’|’,因为3%2=1。即:
jump['-'][0] = jump['|'][1] = 1;
jump['-'][1] = jump['|'][0] = 2;
然后再bfs,在队列中取出一个坐标,pop后再把周围能走的push进去即可。到达目标位置就可以输出答案了。这里为什么不用判断是不是最小呢,因为我最先到达的就肯定是最小了的,不用特意每个min一下。
数据结构:数组、结构体、队列
时间复杂度:O(n*m)
如果要输出路径,参考:BFS最短路径的两种打印方法_罗勇军的博客-CSDN博客_bfs最短路径
三、代码
#include<bits/stdc++.h>
using namespace std;
struct node {
int x,y,step;//记录当前坐标和时间
node(int x = 0,int y = 0,int step = 0)//结构体构造函数
:x(x),y(y),step(step) {
}
};
int n,m;
int step[4][2] = {//方向控制
{-1,0},//上
{1,0},//下
{0,-1},//左
{0,1},//右
};
char grid[30][30];//记录地图
int vis[30][30];//记录是否走过
int jump[300][2];//记录梯子状态
int check(int x,int y){//检查函数
if(x < 0 || x >= n)return 0;
if(y < 0 || y >= m)return 0;
if(vis[x][y])return 0;
return 1;
}
int bfs() {
node now,nex;
queue<node> que;
for(int i=0;i<n;++i){//遍历整个地图
for(int j=0;j<m;++j){
if(grid[i][j] == '*') vis[i][j] = 1;
else if(grid[i][j] == 'S') now = node(i,j);//记录起点
}
}
vis[now.x][now.y] = 1;//起点设置为走过
que.push(now);
while(!que.empty()){
now = que.front();
que.pop();
if(grid[now.x][now.y] == 'T')return now.step;//到达终点就返回当前步数
for(int i=0;i<4;++i){
nex.x = now.x + step[i][0];//往当前方向走一步
nex.y = now.y + step[i][1];//往当前方向走一步
nex.step = now.step;//先获取当前步数用来判断
if(!check(nex.x,nex.y))continue;//如果不能走就跳过
int flag = jump[grid[nex.x][nex.y]][nex.step&1];//用当前格子和时间的奇偶来判断梯子方向
if(flag){//如果flag大于0意味着当前站在梯子上
if((flag == 1 && i >= 2 ) || (flag == 2 && i <= 1)){//如果与梯子方向相同
nex.x += step[i][0];//继续往前走一步
nex.y += step[i][1];//继续往前走一步
if(!check(nex.x,nex.y))continue;//如果梯子后不能走就跳过
}
else nex = now;//如果与梯子方向不同,就原地等待
}
vis[nex.x][nex.y] = 1;//标记此次目的地被走过
nex.step += 1;//时间增加
que.push(nex);//入队
}
}
return 0;//无法走到目标
}
int main() {
jump['-'][0] = jump['|'][1] = 1;//'-' 与 偶数时间 = '|'与奇数时间 = 横向移动
jump['-'][1] = jump['|'][0] = 2;//'-' 与 奇数时间 = '|'与偶数时间 = 竖向移动
while(cin>>n>>m){//循环输入到文件尾
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++){
for(int j=1;j<m;j++){
scanf("%s",grid[i]);//输入地图
}
}
printf("%d\n",bfs());//输出bfs答案
}
return 0;
}