题目链接:https://ac.nowcoder.com/acm/problem/15136
题目描述
这是一个关于二维迷宫的题目。我们要从迷宫的起点 'S' 走到终点 'E',每一步我们只能选择上下左右四个方向中的一个前进一格。 'W' 代表墙壁,是不能进入的位置,除了墙壁以外的地方都可以走。迷宫内的 'D' 代表一道上锁的门,只有在持有钥匙的时候才能进入。而 'K' 则代表了钥匙,只要进入这一格,就会自动地拿到钥匙。最后 '.' 则是代表空无一物的地方,欢迎自在的游荡。
本题的迷宫中,起点、终点、门跟钥匙这四个特殊物件,每一个恰好会出现一次。而且,此迷宫的四周 (最上面的一行、最下面的一行、最左边的一列以及最右边的一列) 都会是墙壁。
请问,从起点到终点,最少要走几步呢?
输入描述:
输入的第一行有两个正整数H, W,分别代表迷宫的长跟宽。
接下来的H行代表迷宫,每行有一个长度恰为W的字串,此字串只包含`'S'`, `'E'`, `'W'`, `'D '`, `'K'`, `'.'`这几种字元。
输出描述:
请在一行中输出一个整数代表答案,如果无法从起点走到终点,请输出-1。
示例1
输入
4 12
WWWWWWWWWWWW
WE.W.S..W.KW
W..D..W....W
WWWWWWWWWWWW
输出
20
示例2
输入
6 6
WWWWWW
WEWS.W
W.WK.W
W.WD.W
W.W..W
WWWWWW
输出
-1
备注:
4 ≤ H, W≤ 500
'S', 'E', 'K', 'D'各出现恰好一次
迷宫的四周(最上面的一行、最下面的一行、最左边的一列以及最右边的一列) 都会是 'W'
思路:
因为问的是最短路的长度,所以是bfs题
但是这个题介入了钥匙和门这俩恶心的玩意就比较坑!
你不可能通过计算一次bfs就得到答案,因为bfs时每个点只能走一次,用完这个点就扔掉了,不会走回头路,这就会导致一种情况:如样例一
4 12 WWWWWWWWWWWW WE.W.S..W.KW W..D..W....W WWWWWWWWWWWW
第三个就踩到门D了,这个时候你没有钥匙,就会进不去,最后就不能到终点。而这个地方的正解是你先去K处拿到钥匙,再去开门到终点,你会发现会重复走,所以就不是一个bfs能解决的了的事!
我们知道要想到达终点分两种情况,一是不进门去走,看看能不能走到终点,另一种是走门再到终点。
而走门又分为两种情况,一是钥匙会在路上捡到,不用刻意去捡,另一种是得先多跑几步找钥匙,再去开门。总的来说,走门得分两步,先bfs从起点到钥匙处,再bfs从钥匙到门,这样得到的肯定是最小步数。
所以,综上所述,解决这个问题可以分两种情况
- 将门的位置当成墙,再用bfs去找
- 先来一步从起点到钥匙的bfs,再来一步从钥匙到门到bfs,再来一步门到终点的bfs,最后三步相加
假设我们bfs没找到最短路时返回的是无穷大,那么如果情况1返回无穷大并且情况二中至少有一个出现无穷大,就说明到不了终点,返回-1
剩下的情况就输出情况一和情况二的最小值
好看一点的AC码
int h, w, x1, yy1, ans, key,xk, yk ,xd, yd;
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};
bool vis[505][505];
char tr[505][505];
struct ran{
int x, y, step;
};
queue<ran>q;
ran over[5];//用来记录点的位置
bool judge(int x, int y){
if(x > h || x < 1 || y > w || y < 1)return false;
else if(vis[x][y])return false;
else if(tr[x][y] == 'W' || tr[x][y] == 'D')return false;
else return true;
}
int bfs(int xx, int yy){
ran now, next;
now.x = xx;now.y = yy;now.step = 0;
q.push(now);
vis[xx][yy] = 1;
while (!q.empty()) {
now = q.front();q.pop();
//一定要把结束的情况放在外面判断鸭,我之前做了一个判能否走出迷宫的题把这个放在for外面wa了,放里面ac了,所以我写的时候就放里面了,结果wa了,呜呜,写了好长时间呢,后来换了个方法写(也就是这个代码),发现和第一遍一样还是每个步数有的差1有的不差1 ,就发现原来是这个地方出了问题!淦
if(now.x == x1 && now.y == yy1){
return now.step;
}
for(int i = 0; i < 4; ++i){
next.x = now.x + dx[i];next.y = now.y + dy[i];
if(!judge(next.x, next.y))continue;
//cout<<now.x<<' '<<now.y<<" -> "<<next.x<<' '<<next.y<<endl;
vis[next.x][next.y] = 1;
next.step = now.step + 1;
q.push(next);
}
}
return inf;
}
void init(){//初始化
memset(vis, 0, sizeof(vis));
while (!q.empty()) {
q.pop();
}
}
int main(){
cin>>h>>w;
for(int i = 1; i <= h; ++i)
for(int j = 1; j <= w; ++j){
scanf(" %c",&tr[i][j]);
if(tr[i][j] == 'S'){//起点
over[1].x = i;over[1].y = j;
}
if(tr[i][j] == 'K'){//钥匙
over[2].x = i;over[2].y = j;
}
if(tr[i][j] == 'D'){//门
over[3].x = i;over[3].y = j;
}
if(tr[i][j] == 'E'){//终点
over[4].x = i;over[4].y = j;
}
}
init();//从起点到终点
x1 = over[4].x;yy1 = over[4].y;
int a = bfs(over[1].x, over[1].y);
init();//从起点到钥匙
x1 = over[2].x;yy1 = over[2].y;
int b = bfs(over[1].x, over[1].y);
init();//从钥匙到门
x1 = over[3].x;yy1 = over[3].y;
tr[over[3].x][over[3].y] = '.';//注意要及时把门给拆了!
int c = bfs(over[2].x, over[2].y);
init();//从门到终点
x1 = over[4].x; yy1 = over[4].y;
int d = bfs(over[3].x, over[3].y);
if(a == inf && (b == inf || c == inf || d == inf))
cout<<-1<<endl;
else cout<<min(a, b + c + d)<<endl;
return 0;
}