티스토리 뷰
문제
풀이 과정의 고민
이 문제는 단순한 구현 문제가 아니라 Union Find의 개념을 이해해야 풀기 쉬웠어요.
Union Find의 개념을 간단히 설명해볼게요.
키 포인트는 노드 간에 부모 - 자식 관계를 만드는 거예요.
즉, 같은 그룹인지 확인하는 방법을 같은 부모를 가지고 있는지 확인하는 것으로 대신하는 거죠.
쉽게 생각하면 같은 그룹에 속할 경우 그 요소들에 그룹 번호를 부여한다고 볼 수도 있는데, 그 그룹 번호가 새롭게 생성되는 것이 아닌 특정 요소를 부모로 해서 그 요소 자체를 그룹 번호로 설정하는 거예요.
예시를 통해 살펴볼게요.
아래와 같이 4개의 노드가 존재한다고 가정해요.
이 상태에서는 어떤 연결도 없기 때문에 각 노드의 부모는 자기 자신이 될 거예요.
노드 | 1 | 2 | 3 | 4 |
부모 | 1 | 2 | 3 | 4 |
그런데 1번 4번이 연결된 상태가 추가된다고 하면
4번 노드의 부모 노드를 1번 노드로 변경해요.
노드 | 1 | 2 | 3 | 4 |
부모 | 1 | 2 | 3 |
이제 부모 노드를 살펴보았을 때 [1, 2, 3] 세 가지 종류의 요소가 존재하므로 총그룹의 수는 3가지가 될 거예요.
이걸 코드로 풀어볼게요. 편의상 1, 2, 3, 4를 인덱스대로 0, 1, 2, 3에 매칭 시켜 구현해볼게요.
def find(x):
if x == parent[x]:
return x
else:
my_parent = find(parent[x])
parent[x] = my_parent
return parent[x]
def union(x, y):
x = find(x)
y = find(y)
parent[y] = x
parent = [i for i in range(4)]
union(0, 3) # 1번 노드와 4번 노드를 연결
print(len(set(parent))) # 3 출력
여기서 헷갈릴 수 있는 부분은 연속해서 Union을 할 때 어떤 것을 parent로 둘 지의 기준이에요.
뭔가 막연하게 1과 4를 연결했을 때 4의 부모를 1로 두는 것과 1의 부모를 4로 두는 것이 다른 결과를 낼 수 있다고 생각이 들기 때문이에요. 그런데 Union Find에서 찾고자 하는 것은 누가 부모냐의 문제가 아니라 그룹의 개수이기 때문에 문제가 되지 않아요. 위의 예시에서 부모가 [1, 2, 3, 1]이 되는 것이나 [4, 2, 3, 4]가 되는 것이나 모두 결론은 3개가 돼서 같은 결론이 돼요 :]
이제 이 개념을 기반으로 문제를 풀어볼게요!
풀이
def find(x):
if x == parent[x]:
return x
else:
my_parent = find(parent[x])
parent[x] = my_parent
return parent[x]
def union(x, y):
parent_x = find(x)
parent_y = find(y)
if parent_x != parent_y:
parent[parent_y] = parent_x
number[parent_x] += number[parent_y] # 그룹에 인원 수 추가
from sys import stdin
input = stdin.readline
T = int(input())
for _ in range(T):
parent, number = {}, {}
F = int(input())
for _ in range(F):
f1, f2 = input().split()
if f1 not in parent:
parent[f1] = f1
number[f1] = 1
if f2 not in parent:
parent[f2] = f2
number[f2] = 1
union(f1, f2)
print(number[find(f1)])
문제 풀이 해설
위의 풀이를 기준으로 parent와 number를 출력해볼게요.
여기서 헷갈릴 수 있는 부분은 number에 나타난 숫자 값이에요.
저 자료를 단순히 "Betty: 1"이라고 해서 "Betty라는 parent가 1명"이라고 해석하면 안돼요.
기준을 Wilma로 세웠기 때문에 Wilma의 number에 Betty의 number가 더해지는 순간 "Betty:1"은 의미가 없는 값이 돼요. 어차피 Betty에 접근해도 그 부모인 Wilma의 숫자로 계산될 것이기 때문이에요.
그래서 풀이에서 중요한 것은 union의 방향인데 union(x, y)에서 number[x] <- number[y] 방향으로 흡수되는 구조이기 때문에 마지막 출력도 반드시 왼쪽 요소인 f1을 출력해줘야 해요. 여기서 f2를 출력하면 union이 반영되지 않은 값이 나오는 것이죠.
즉, number라는 자료는 단순히 각 키를 parent로 하는 자료들의 집합이 아니라 병합되기 이전의 그룹 수(Dummy 값)도 포함한 자료이므로 꺼내올 때 그 순서를 잘 고려해야 하는 자료인 것이죠.
또 중요한 것은 parent 자료인데 Betty의 parent는 여전히 Fred가 아닌 Wilma라는 것을 확인할 수 있죠. 즉, Union Find에서는 자료를 매번 갱신해서 최상위 parent로 만들어주는 것이 아니라 바로 위 노드 부모는 유지하되 검색할 때에는 찾을 수 있게 만들어주는 구조예요.
문제 출처
https://www.acmicpc.net/problem/4195
'알고리즘 학습 > 백준 알고리즘' 카테고리의 다른 글
[백준 알고리즘] - 트리 순회(1991번) with Swift (0) | 2022.01.16 |
---|---|
[백준 알고리즘] - 프린터 큐 (1966번) with Python (0) | 2022.01.02 |
[백준 알고리즘] - 명령 프롬프트(1032번) with Python (0) | 2021.07.12 |
[백준 알고리즘] - 가장 긴 증가하는 부분 수열 2 (12015번) with Python (0) | 2021.06.16 |
[백준 알고리즘] - 트리의 부모 찾기 (11725번) with Python (0) | 2021.04.20 |
- Total
- Today
- Yesterday
- API#lazy#
- 랜선자르기#이분탐색#BOJ#Python
- 나무자르기#BOJ#이분탐색#Python
- Brackets#Stacks and Queues#Codility#Python
- 쿼드트리#BOJ#분할정복#Python
- 터틀비치#리콘#xbox#controller
- 배열합치기#분할정복#BOJ#Python
- django#slicing
- N으로 표현#DP#Programmers#Python
- Distinct#Codility#Python
- 병든 나이트#BOJ#탐욕법#Python
- 토마토#백준알고리즘#Python
- PassingCars#Codility#Python
- Swift#Tuples#Range
- 종이자르기#분할정복#BOJ#Python
- 날짜 계산#BOJ#완전탐색#Python
- django
- 미로 탐색#백준알고리즘#Python
- NumberofDiscIntersections#Codility#Sort#Python
- 공유기 설치#BOJ#이분탐색#Python
- 리모컨#완전탐색#BOJ#Python
- 반복수열#백준알고리즘#Python
- filter#isalnum#lower
- 파이썬알고리즘인터뷰#4장
- 텀 프로젝트#백준알고리즘#Python
- 암호코드#dp#BOJ#Python
- 순열사이클#BOJ#Python
- 백준 알고리즘#BackTracking
- 섬의개수#백준알고리즘#Python
- Triangle#Sorting#Codility#Python
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |