[프로그래머스] 거리두기 확인하기 (2021 카카오 인턴십) / JavaScript
거리두기 확인하기
https://programmers.co.kr/learn/courses/30/lessons/81302
코딩테스트 연습 - 거리두기 확인하기
[["POOOP", "OXXOX", "OPXPX", "OOXOX", "POXXP"], ["POOPX", "OXPXP", "PXXXO", "OXXXO", "OOOPP"], ["PXOPX", "OXOXP", "OXPOX", "OXXOP", "PXPOX"], ["OOOXX", "XOOOX", "OOOXX", "OXOOX", "OOOOO"], ["PXPXP", "XPXPX", "PXPXP", "XPXPX", "PXPXP"]] [1, 0, 1, 1, 1]
programmers.co.kr
문제 설명
개발자를 희망하는 죠르디가 카카오에 면접을 보러 왔습니다.
코로나 바이러스 감염 예방을 위해 응시자들은 거리를 둬서 대기를 해야하는데 개발 직군 면접인 만큼
아래와 같은 규칙으로 대기실에 거리를 두고 앉도록 안내하고 있습니다.
- 대기실은 5개이며, 각 대기실은 5x5 크기입니다.
- 거리두기를 위하여 응시자들 끼리는 맨해튼 거리1가 2 이하로 앉지 말아 주세요.
- 단 응시자가 앉아있는 자리 사이가 파티션으로 막혀 있을 경우에는 허용합니다.
예를 들어,
위 그림처럼 자리 사이에 파티션이 존재한다면 맨해튼 거리가 2여도 거리두기를 지킨 것입니다.
위 그림처럼 파티션을 사이에 두고 앉은 경우도 거리두기를 지킨 것입니다.
위 그림처럼 자리 사이가 맨해튼 거리 2이고 사이에 빈 테이블이 있는 경우는 거리두기를 지키지 않은 것입니다.
5개의 대기실을 본 죠르디는 각 대기실에서 응시자들이 거리두기를 잘 기키고 있는지 알고 싶어졌습니다. 자리에 앉아있는 응시자들의 정보와 대기실 구조를 대기실별로 담은 2차원 문자열 배열 places
가 매개변수로 주어집니다. 각 대기실별로 거리두기를 지키고 있으면 1
을, 한 명이라도 지키지 않고 있으면 0
을 배열에 담아 return 하도록 solution
함수를 완성해 주세요.
제한사항
places
의 행 길이(대기실 개수) = 5places
의 각 행은 하나의 대기실 구조를 나타냅니다.
places
의 열 길이(대기실 세로 길이) = 5places
의 원소는P
,O
,X
로 이루어진 문자열입니다.places
원소의 길이(대기실 가로 길이) = 5P
는 응시자가 앉아있는 자리를 의미합니다.O
는 빈 테이블을 의미합니다.X
는 파티션을 의미합니다.
- 입력으로 주어지는 5개 대기실의 크기는 모두 5x5 입니다.
- return 값 형식
- 1차원 정수 배열에 5개의 원소를 담아서 return 합니다.
places
에 담겨 있는 5개 대기실의 순서대로, 거리두기 준수 여부를 차례대로 배열에 담습니다.- 각 대기실 별로 모든 응시자가 거리두기를 지키고 있으면 1을, 한 명이라도 지키지 않고 있으면 0을 담습니다.
풀이
function solution(places) {
const move = [[0, -1],[0, 1],[1, 0],[-1, 0]];
const SIZE = 5;
const isValid = (x, y) => x >= 0 && y >= 0 && x < SIZE && y < SIZE;
const isAvailableSeat = (x, y, checked) =>
isValid(x, y) && checked[x][y] === 0;
// bfs
const checkAround = (start, room, checked) => {
let queue = [start];
while (queue.length > 0) {
const [x, y, n] = queue.shift();
if (n !== 0 && room[x][y] === "P") return false;
move.forEach(([mx, my]) => {
const _x = x + mx;
const _y = y + my;
if (isAvailableSeat(_x, _y, checked) && room[_x][_y] != "X") {
if (n < 2) {
checked[_x][_y] = 1;
queue.push([_x, _y, n + 1]);
}
}
});
}
return true;
};
const checkDistancing = (room) => {
let checked = Array.from({ length: SIZE }, () => Array(SIZE).fill(0));
for (let i = 0; i < SIZE; i++) {
for (let j = 0; j < SIZE; j++) {
if (room[i][j] !== "P") continue;
checked[i][j] = 1;
if (!checkAround([i, j, 0], room, checked)) return 0;
}
}
return 1;
};
return places.map(checkDistancing);
}
- 하나의 칸을 정점으로 본다.
- 파티션이 있는 칸은 간선이 없다고 인식한다.
그러면 이 문제는 사람이 있는 정점에서 거리 2 이내에 다른 사람이 있는 정점이 있는지를 검사하는 그래프 탐색 문제가 된다.
dfs 또는 bfs로 풀 수 있는 문제인데 여기서는 bfs로 풀었다.
const checkDistancing = (room) => {
let checked = Array.from({ length: SIZE }, () => Array(SIZE).fill(0));
for (let i = 0; i < SIZE; i++) {
for (let j = 0; j < SIZE; j++) {
if (room[i][j] !== "P") continue;
checked[i][j] = 1;
if (!checkAround([i, j, 0], room, checked)) return 0;
}
}
return 1;
};
checkDistancing
은 이중 for문을 사용해 모든 대기실의 자리를 순회 하면서 자리(P
)를 찾는 함수이다.
자리(P
)를 만나면 해당 자리의 좌표를 시작점으로 bfs 탐색을 한다.
checked
함수는 bfs 탐색 시 정점의 방문 여부를 체크해주기 위한 배열이며, 먼저 시작점을 방문했다고 표시 후(checked[i][j] = 1
) bfs 탐색을 시작한다.
// bfs
const checkAround = (start, room, checked) => {
let queue = [start];
while (queue.length > 0) {
const [x, y, n] = queue.shift();
if (n !== 0 && room[x][y] === "P") return false;
move.forEach(([mx, my]) => {
const _x = x + mx;
const _y = y + my;
if (isAvailableSeat(_x, _y, checked) && room[_x][_y] != "X") {
if (n < 2) {
checked[_x][_y] = 1;
queue.push([_x, _y, n + 1]);
}
}
});
}
return true;
};
bfs탐색을 위한 queue
를 만들고 시작점을 넣어준다.
queue
에는 [x, y, n]
의 형태로 들어가는데, 각각 정점의 x좌표, y좌표, 거리 를 나타낸다.
if (n !== 0 && room[x][y] === "P") return false
탐색 중 다른 자리(P
)를 만나면 바로 false
를 리턴해 반복문을 빠져나온다. 시작점은 항상 P
이기 때문에 n !== 0
조건을 추가해주었다.
시작점에서 상,하,좌,우의 정점을 탐색한다.
const isValid = (x, y) => x >= 0 && y >= 0 && x < SIZE && y < SIZE;
const isAvailableSeat = (x, y, checked) => isValid(x, y) && checked[x][y] === 0;
범위를 벗어나지 않고, 아직 방문하지 않은 곳 이면서 파티션이 아니라면 방문 표시를 하고 queue
에 넣어준다.
queue
가 비워지고 while문을 통과했다면, 거리두기를 준수했다는 의미이므로 true
를 리턴한다.