📌 문제
우리 나라는 가족 혹은 친척들 사이의 관계를 촌수라는 단위로 표현하는 독특한 문화를 가지고 있다. 이러한 촌수는 다음과 같은 방식으로 계산된다. 기본적으로 부모와 자식 사이를 1촌으로 정의하고 이로부터 사람들 간의 촌수를 계산한다. 예를 들면 나와 아버지, 아버지와 할아버지는 각각 1촌으로 나와 할아버지는 2촌이 되고, 아버지 형제들과 할아버지는 1촌, 나와 아버지 형제들과는 3촌이 된다.
여러 사람들에 대한 부모 자식들 간의 관계가 주어졌을 때, 주어진 두 사람의 촌수를 계산하는 프로그램을 작성하시오.
📌 입력
사람들은 1, 2, 3, …, n (1 ≤ n ≤ 100)의 연속된 번호로 각각 표시된다. 입력 파일의 첫째 줄에는 전체 사람의 수 n이 주어지고, 둘째 줄에는 촌수를 계산해야 하는 서로 다른 두 사람의 번호가 주어진다. 그리고 셋째 줄에는 부모 자식들 간의 관계의 개수 m이 주어진다. 넷째 줄부터는 부모 자식간의 관계를 나타내는 두 번호 x,y가 각 줄에 나온다. 이때 앞에 나오는 번호 x는 뒤에 나오는 정수 y의 부모 번호를 나타낸다.
각 사람의 부모는 최대 한 명만 주어진다.
📌 출력
입력에서 요구한 두 사람의 촌수를 나타내는 정수를 출력한다. 어떤 경우에는 두 사람의 친척 관계가 전혀 없어 촌수를 계산할 수 없을 때가 있다. 이때에는 -1을 출력해야 한다.
📌 예제 입출력
📌 풀이
알아야 하는 것
- 할아버지-아버지, 아버지-나 이렇게 깊게 내려오는 것(1촌) 뿐만 아니라 아버지의 형제(3촌), 나의 형제(2촌)도 계산해야 한다. DFS로 풀게 되면 끝까지 내려간 후 다시 되돌아와 너비도 탐색해야 하는데, BFS로 하면 자기 자신에서부터 한 단계씩 인접한 노드들을 찾아가면서 구하기 때문에 더 효율적이라고 판단했다.
- 실제로 JS는 되도록 DFS보다 BFS로 접근하는 것이 안전하다. (간단히 알아보니 JS는 실행 스택을 사용해 함수 호출을 관리하는데, 크기가 제한적이라 너무 깊은 재귀 호출이 반복되면 스택 오버 플로우로 에러가 발생한다. 🫠)
구할 것
- 백준 입력값 가공
- 입력값의 첫 번째 값인 노드의 수를 shift로 떼어내 상수 numOfNode로 선언한다.
- shift로 떼어냈기 때문에 이제 입력값의 첫 번째 값이 된 7, 3을 구조 분해 할당으로 [one, two]에 할당한다. 이 부분이 관계를 구하려는 노드다.
- 이후 가족 간의 관계를 설명하는 간선은 상수 family에 할당한다.
- 그래프 구성 및 BFS 구현
- 그래프를 구현하고 queue를 선언한 후 방문 여부를 체크할 visited 배열을 선언한다.
- 양방향으로 간선의 결과를 양쪽에 넣어준다.
- queue에 처음 시작할 값을 넣는다. 이 때 탐색을 시작할 노드는 노드 1이 아니라 문제에서 구하려는 노드로, one이나 two를 기준으로 한다. (코드에서는 one을 기준으로 했다.)
- one을 queue에 넣고 방문 처리 (본인 기준 1) 하고, queue에 넣어준다. shift로 이 값을 뺐을 때 나와 연결된 노드들을 하나씩 순회한다. 이 때 방문하지 않은 노드들만 queue에 넣고, visited 배열에 방문했다는 뜻으로 +1을 추가한다. 예를 들어 노드 7과 연결된 값이 2인데, 이 2는 노드 7 기준으로 거리가 1 차이나는 부모다.
- 노드 2를 탐색하게 될 때 1은 노드 7 기준 거리가 2 차이 나니 토탈 3의 값을 갖는다. 다음 노드 7을 탐색할 때 노드 7 기준 토탈 4의 값을 갖게 된다. (노드 7 > 노드 2 > 노드 1 > 노드 3)
- 이 때 노드 7에서 노드 3을 빼든 반대든 양수가 나와야 하므로 Math.abs로 절댓값을 가져왔다.
- 그런데 아래와 같이 그래프 상에서 0이 나오는 경우가 있다. 연결되지 않은 그래프의 경우 -1을 출력해야 해서 한 쪽이라도 값이 0이라면 연결되지 않은 것이니 -1을 반환하도록 조건문을 추가해줬다.
그래프의 시작점을 습관적으로 1번 노드로 했는데, 그렇게 하니까 테스트 케이스별로 값이 다 제각기라서 한참 헤맸다. 생각해보면 기준이 명확하게 있는 문제인데 템플릿대로 문제를 풀되 생각이라는 걸 좀 해야겠다.
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");
// 노드 수
const numOfNode = Number(input.shift());
// 관계를 알아야 하는 번호
const [one, two] = input[0].split(" ").map(Number);
const family = input.slice(2).map((v) => v.split(" ").map(Number));
const graph = Array.from(Array(numOfNode + 1), () => []);
const visited = Array(numOfNode + 1).fill(0);
for (let [from, to] of family) {
graph[from].push(to);
graph[to].push(from);
}
function bfs(graph, start) {
const queue = [];
queue.push(start);
visited[start] = 1;
while (queue.length) {
const node = queue.shift();
for (let next of graph[node]) {
if (!visited[next]) {
queue.push(next);
visited[next] = visited[node] + 1;
}
}
}
if (visited[one] === 0 || visited[two] === 0) {
return -1;
} else {
return Math.abs(visited[one] - visited[two]);
}
}
console.log(bfs(graph, one));
'⚙️ 코딩테스트' 카테고리의 다른 글
[JavaScript] 프로그래머스 코딩테스트 레벨 3 : 네트워크 (0) | 2023.07.12 |
---|---|
[JavaScript] 프로그래머스 코딩테스트 레벨 3 : 여행 경로 (0) | 2023.07.11 |
[JavaScript] 백준 코딩테스트 2178. 실버 1 : 미로 탐색 (0) | 2023.07.07 |
[JavaScript] 백준 코딩테스트 2606. 실버 3 : 바이러스 (0) | 2023.07.06 |
[JavaScript] 백준 코딩테스트 1260. 실버 2 : DFS와 BFS (0) | 2023.07.06 |