1 x 1 크기의 칸들로 이루어진 직사각형 격자 형태의 미로에서 탈출하려고 합니다. 각 칸은 통로 또는 벽으로 구성되어 있으며, 벽으로 된 칸은 지나갈 수 없고 통로로 된 칸으로만 이동할 수 있습니다. 통로들 중 한 칸에는 미로를 빠져나가는 문이 있는데, 이 문은 레버를 당겨서만 열 수 있습니다. 레버 또한 통로들 중 한 칸에 있습니다. 따라서, 출발 지점에서 먼저 레버가 있는 칸으로 이동하여 레버를 당긴 후 미로를 빠져나가는 문이 있는 칸으로 이동하면 됩니다. 이때 아직 레버를 당기지 않았더라도 출구가 있는 칸을 지나갈 수 있습니다. 미로에서 한 칸을 이동하는데 1초가 걸린다고 할 때, 최대한 빠르게 미로를 빠져나가는데 걸리는 시간을 구하려 합니다.
미로를 나타낸 문자열 배열 maps
가 매개변수로 주어질 때, 미로를 탈출하는데 필요한 최소 시간을 return 하는 solution 함수를 완성해주세요. 만약, 탈출할 수 없다면 -1을 return 해주세요.
maps
의 길이 ≤ 100
maps[i]
의 길이 ≤ 100maps[i]
는 다음 5개의 문자들로만 이루어져 있습니다.
우선 최소 시간을 구하는 문제이므로 BFS로 풀어야겠다고 생각했다.
미로를 탈출하기 위해서는 조건이 걸려있었다.
이전에 풀었던 게임 맵 최단거리 문제와 비슷하게 접근하면 될 것 같다고 생각해 바로 풀어보았지만 역시 안되었다. 그 이유는 레버보다 출구를 먼저 발견했을 경우 찾아도 레버를 당기지 않았으므로 탈출할 수 없기 때문이다.
그러면 출구를 찾아도 레버를 찾으러 지나쳤다가 당기고 돌아와야한다는 건데... 너무 어려웠다.
나는 그래서 2가지 방법으로 접근했다.
function solution(maps) {
const [row, column] = [maps.length, maps[0].length];
// 레버까지 이동하기 위한 배열
const lArr = Array.from(Array(row), (e) => Array(column).fill(0));
// 출구까지 이동하기 위한 배열
const eArr = Array.from(Array(row), (e) => Array(column).fill(0));
// 상 우 하 좌로 이동하기 위한 배열
const position = [[1,0],[0,1],[-1,0],[0,-1]];
let need = [];
// 레버와 출구, 시작 위치
let l = [];
let e = [];
let s = [];
// 레버와 출구의 도착 유무
let isL = false;
let isE = false;
// 시작 위치, 레버 위치, 출구 위치 저장
maps.forEach((v,i) => {
if(v.includes("S")) s.push(i,v.indexOf("S"));
if(v.includes("L")) l.push(i,v.indexOf("L"));
if(v.includes("E")) e.push(i,v.indexOf("E"));
})
// 시작 위치에 1을 저장
lArr[s[0]][s[1]] = 1;
need.push(s)
while((!isL || !isE) && need.length){
// [row, column] 현재 좌표
const [r, c] = need.shift();
// 레버를 당기기 위함
if(!isL){
for(let i = 0; i < 4; i++){
const [newR, newC] = [r+position[i][0], c+position[i][1]];
if(newR < 0 || newC < 0 || newR >= row || newC >= column) continue;
if(!lArr[newR][newC] && maps[newR][newC] !== "X"){
// 이전 위치 값에서 1을 더해줌으로써 해당 위치까지의 거리를 알 수 있음
lArr[newR][newC] = lArr[r][c] + 1;
need.push([newR, newC]);
}
// 레버를 당겼으면 레버 위치를 need에 넣어주고 이제 출구를 찾음
if(lArr[l[0]][l[1]]) {
isL = true;
need = [l];
eArr[l[0]][l[1]] = 1;
}
}
}else{
// 출구를 찾기 위함
for(let i = 0; i < 4; i++){
const [newR, newC] = [r+position[i][0], c+position[i][1]];
if(newR < 0 || newC < 0 || newR >= row || newC >= column) continue;
if(!eArr[newR][newC] && maps[newR][newC] !== "X"){
eArr[newR][newC] = eArr[r][c] + 1;
need.push([newR, newC]);
}
if(eArr[e[0]][e[1]]) {
isE = true;
need = [];
}
}
}
}
// 각 시작 위치에 1을 넣고 시작했으므로 -2를 해줌
return eArr[e[0]][e[1]] ? lArr[l[0]][l[1]] + eArr[e[0]][e[1]] - 2 : -1;
}
아래 사진은 이해를 돕기 위해 레버를 찾는 맵과 출구를 찾는 맵을 콘솔로 찍은 사진입니다.