카테고리 없음

99클럽 코테 스터디 8일차 TIL - [BFS] : 백준 2644 촌수계산

레야_Reya 2024. 11. 5. 10:59
오늘의 문제

 

백준 2644 : 촌수계산

(https://www.acmicpc.net/problem/2644)

문제 설명
우리 나라는 가족 혹은 친척들 사이의 관계를 촌수라는 단위로 표현하는 독특한 문화를 가지고 있다. 이러한 촌수는 다음과 같은 방식으로 계산된다. 기본적으로 부모와 자식 사이를 1촌으로 정의하고 이로부터 사람들 간의 촌수를 계산한다. 예를 들면 나와 아버지, 아버지와 할아버지는 각각 1촌으로 나와 할아버지는 2촌이 되고, 아버지 형제들과 할아버지는 1촌, 나와 아버지 형제들과는 3촌이 된다.
여러 사람들에 대한 부모 자식들 간의 관계가 주어졌을 때, 주어진 두 사람의 촌수를 계산하는 프로그램을 작성하시오.

입력

사람들은 1, 2, 3, …, n (1 ≤ n ≤ 100)의 연속된 번호로 각각 표시된다. 입력 파일의 첫째 줄에는 전체 사람의 수 n이 주어지고, 둘째 줄에는 촌수를 계산해야 하는 서로 다른 두 사람의 번호가 주어진다. 그리고 셋째 줄에는 부모 자식들 간의 관계의 개수 m이 주어진다. 넷째 줄부터는 부모 자식간의 관계를 나타내는 두 번호 x,y가 각 줄에 나온다. 이때 앞에 나오는 번호 x는 뒤에 나오는 정수 y의 부모 번호를 나타낸다.
각 사람의 부모는 최대 한 명만 주어진다.

출력
입력에서 요구한 두 사람의 촌수를 나타내는 정수를 출력한다. 어떤 경우에는 두 사람의 친척 관계가 전혀 없어 촌수를 계산할 수 없을 때가 있다. 이때에는 -1을 출력해야 한다.


코드
from collections import deque
import sys

total_people = int(sys.stdin.readline())   # 전체 사람 수
person1, person2 = map(int, sys.stdin.readline().split())   # 촌수 계산할 두 사람 번호
total_relations = int(sys.stdin.readline())   # 부모 자식 관계의 총 개수

# 가족 관계 그래프: 각 사람별로 연결된 가족들을 저장
family_graph = [[] for _ in range(total_people+1)]
for _ in range(total_relations):
    parent,child = map(int, sys.stdin.readline().split())
    family_graph[parent].append(child)
    family_graph[child].append(parent)   # 양방향 연결

checked_people = [0] * (total_people+1)   # 각 사람을 확인했는지 체크하는 리스트

def calculate_relation(start_person, target_person):
    chon = 0   # 촌수를 저장할 변수
    checked_people[start_person] = 1   # 시작 사람 체크
    queue = deque([(start_person,chon)])   # (사람번호, 현재까지 촌수)를 큐에 저장

    while queue:
        current_person, current_chon = queue.popleft()   # 큐에서 현재 확인할 사람 꺼내기

        if current_person == target_person:
            return current_chon

        # 현재 사람과 연결된 모든 가족 확인
        for relative in family_graph[current_person]:
            if checked_people[relative] == 0 :   # 아직 확인하지 않은 가족이면
                checked_people[relative] = 1   # 체크하고
                queue.append((relative,current_chon+1))   # 큐에 추가 (촌수 1 증가)

    return -1   # 관계가 없는 경우

print(calculate_relation(person1,person2))

 

문제 풀이

 

BFS란?
BFS(Breadth-First Search)는 '너비 우선 탐색'이라고도 하며, 그래프를 탐색할 때 가까운 노드부터 먼저 탐색하는 알고리즘이다. 마치 물결이 퍼져나가듯이 시작점에서 가까운 정점부터 차례로 탐색한다.


왜 오늘의 문제 촌수계산에 BFS를 사용할까?
촌수계산은 두 사람 사이의 '최단 거리'를 구하는 문제와 같기 때문이다.

예를 들어서, 촌수는 관계의 가지에 따라서 아래와 같이 결정되는데,
- 부모-자식 간: 1촌
- 나-할아버지 간: 2촌 (부모를 거쳐 할아버지까지)
- 나-사촌 간: 3촌 (부모, 할아버지, 삼촌을 거침)
그러므로 촌수계산을 위해서 최단 거리를 구하는데 최적화된 BFS 알고리즘이 적합하다.

 

문제 해결 접근 방법을 풀어서 써보겠다.

1. 가족 관계를 그래프로 표현하기

family_graph = [[] for _ in range(total_people + 1)]
for _ in range(total_relations):
    parent, child = map(int, sys.stdin.readline().split())
    family_graph[parent].append(child)
    family_graph[child].append(parent)    # 양방향 연결

 

  • 각 사람을 노드로, 가족 관계를 간선으로 표현한다.
  • 부모-자식 관계는 양방향으로 연결 (부모→자식, 자식→부모)

 

2. BFS 구현의 핵심 요소

def calculate_relation(start_person, target_person):
    chon = 0   # 촌수 (거리)
    checked_people = [0] * (total_people + 1)   # 방문 체크
    queue = deque([(start_person, chon)])  # BFS를 위한 큐

 

  • 큐(Queue)를 사용하여 방문할 노드 관리
  • 방문 체크 배열로 중복 방문 방지
  • 각 노드까지의 거리(촌수) 기록

 

3. BFS 탐색 과정

while queue:
        current_person, current_chon = queue.popleft()
        
        if current_person == target_person:
            return current_chon
            
        for relative in family_graph[current_person]:
            if checked_people[relative] == 0:
                checked_people[relative] = 1
                queue.append((relative, current_chon + 1))
  • BFS의 작동 원리에 따라 다음과 같이 진행된다.
    1) 큐에서 현재 확인할 사람을 꺼냄
    2) 찾는 사람인지 확인
    3) 아니라면 이 사람과 직접 연결된 가족들을 큐에 추가
    4) 이때 촌수는 1씩 증가

오늘의 회고

 

그동안 BFS 문제가 나올 때마다 여러 블로그 글을 보고 알고리즘에 대해서 공부하면서 코드들을 많이 참고해서 풀었다.

그런데 여러번 풀어도 계속해서 뭔가 푸는 방식이 잘 와닿지 않고 헷갈렸던 것 같다.

오늘은 그래서 평소랑 다르게 변수명을 좀 길더라도 직관적으로 써보았는데

이렇게 하니까 코드의 작동 순서나 이 변수가 어디에 왜 들어가는지가 훨씬 잘 이해가 되는 것 같다.

처음으로 특정한 알고리즘을 공부하고 코드의 구조를 익히기 시작하는 단계에서는 이렇게 다소 길고 불편하더라도,

변수명을 정확하게 표기해서 문제를 푸는 연습을 하면 좋은 것 같다.

그리고 수차례 비슷한 문제들을 풀어보고 어느정도 기계적으로 머릿속에 구조화가 잘 되고 난 다음부터

코드를 단순화하기 위해 심플한 변수명을 사용하는 것이 낫겠다는 생각을 했다.

나는 알고리즘 초보자인 만큼 이해하는 데 시간을 많이 들이자...! :)