21276번: 계보 복원가 호석
석호촌에는 N 명의 사람이 살고 있다. 굉장히 활발한 성격인 석호촌 사람들은 옆 집 상도 아버님, 뒷집 하은 할머님 , 강 건너 유리 어머님 등 모두가 한 가족처럼 살아가고 있다. 그러던 어느 날
www.acmicpc.net
문제
석호촌에는 N 명의 사람이 살고 있다. 굉장히 활발한 성격인 석호촌 사람들은 옆 집 상도 아버님, 뒷집 하은 할머님 , 강 건너 유리 어머님 등 모두가 한 가족처럼 살아가고 있다.
그러던 어느 날, 유구한 역사를 지닌 석호촌의 도서관에 화재가 나서 계보들이 모두 불타고 말았다. 그래도 계보는 있어야 하지 않겠느냐는 마을 어르신인 대일 촌장님의 의견에 따라 석호촌 사람들은 계보 복원가 호석에게 의뢰를 맡기기로 했다.
적어도 현재를 함께 살아가는 N 명의 계보는 복원하고 싶은 호석이는 조사를 통해서 각자가 기억하는 조상들의 이름들을 구해냈다. 다행히도 석호촌의 맑은 정기 덕분에 기억력이 굉장히 좋은 주민들은 모든 조상을 완벽하게 기억하고 있다. 또한, 각 가문은 한 명의 시조를 root로 하는 트리 형태를 띈다는 것도 알아냈다. 이 때 "조상"이란, "자신의 부모"와 "부모의 조상"을 모두 합친 것을 의미한다.
이를 기반으로 몇 개의 가문이 존재했는 지, 각 가문에 대한 정보를 출력하는 프로그램을 작성해서 호석이를 도와주자!
입력
첫번째 줄에 석호촌에 살고 있는 사람의 수 N 이 주어진다. 두번째 줄에는 현재 살고 있는 사람들의 이름이 차례대로 주어진다. 모든 이름은 길이 1 이상 6 이하의 알파벳 소문자로 이뤄지며, 중복된 이름은 존재하지 않는다.
세번째 줄에는 기억하는 정보의 개수 M 이 주어진다. 이어지는 M개의 줄에는 "X Y" 꼴로 기억들이 주어지는데, 이는 곧 X의 조상 중에 Y가 있다는 것을 의미하며 같은 정보가 중복되어 주어지지 않는다. 입력에 모순이 있는 경우는 주어지지 않는다.
출력
첫번째 줄에는 가문의 개수 K 를 출력하라. 두 번째 줄에는 각 가문의 시조들의 이름을 공백으로 구분하여 사전순으로 출력하라.
세번째 줄부터는 이름의 사전순 대로 사람의 이름과 자식의 수, 그리고 사전순으로 자식들의 이름을 공백으로 구분하여 출력하라.
출력
- 1 ≤ N ≤ 1,000
- 0 ≤ M ≤ N×(N-1)/2
코드
from collections import defaultdict, deque
import sys
input = sys.stdin.readline
n = int(input())
people = list(input().split())
m = int(input())
graph = defaultdict(list) # 후손 리스트 저장
indegree = defaultdict(int) # 진입차수
for _ in range(m):
x, y = input().split() # x의 조상 y
indegree[x] += 1
graph[y].append(x) # y의 후손 x
# 진입차수가 0인 노드 = 시조
q = deque()
for p in people:
if indegree[p] == 0:
q.append(p)
# 시조 출력
print(len(q))
print(*sorted(q))
family = {i: [] for i in people} # 직접적인 부모-자식 관계
# 위상정렬
while q:
now = q.popleft()
for child in graph[now]:
indegree[child] -= 1
if indegree[child] == 0:
family[now].append(child)
q.append(child)
# 부모-자식 관계 출력
for parent, child in sorted(family.items()):
print(parent, len(child), *sorted(child))
위상정렬을 이용하여 풀었다.
처음에는 union-find를 이용하여 MST로 족보를 만드려고 했으나 시조는 잘 찾았지만 부모-자식 관계를 찾는것이 너무 복잡하여 위상정렬을 이용하기로 했다. 조상->후손으로 방향이 있는 그래프이기 때문에 위상정렬을 사용할 수 있다.
indegree에는 해당 노드의 진입차수, 즉 조상의 수를 저장한다.
graph는 딕셔너리로, key인 사람의 후손들이 value로 저장된다.
모든 가족관계 정보가 주어지고 난 뒤 진입 차수가 0인 노드들이 바로 시조이다. 시조들을 q에 넣어주고 위상정렬을 수행한다.
- 이미 살펴본 현재 노드(now)와 자식 노드들 간의 관계를 끊어주어야 한다. 그러기 위해서 자식 노드의 진입차수를 1 감소시킨다. 이때 진입차수가 0이 되는 노드는 현재 노드의 직접적인 자식이므로 q에 넣어주고, 직접적인 부모-자식 관계를 기록하는 family[now]도 넣어준다.
'문제풀이 > 위상정렬' 카테고리의 다른 글
[Python/파이썬] 백준 1516번 게임 개발 (0) | 2025.01.29 |
---|---|
[Python/파이썬] 백준 9470번 Strahler 순서 (1) | 2023.12.07 |
[Python/파이썬] 백준 14676번 영우는 사기꾼? (0) | 2023.05.18 |
[Python/파이썬] 백준 2056번 작업 (0) | 2023.05.17 |
[Python/파이썬] 백준 1766번 문제집 (0) | 2023.04.01 |
21276번: 계보 복원가 호석
석호촌에는 N 명의 사람이 살고 있다. 굉장히 활발한 성격인 석호촌 사람들은 옆 집 상도 아버님, 뒷집 하은 할머님 , 강 건너 유리 어머님 등 모두가 한 가족처럼 살아가고 있다. 그러던 어느 날
www.acmicpc.net
문제
석호촌에는 N 명의 사람이 살고 있다. 굉장히 활발한 성격인 석호촌 사람들은 옆 집 상도 아버님, 뒷집 하은 할머님 , 강 건너 유리 어머님 등 모두가 한 가족처럼 살아가고 있다.
그러던 어느 날, 유구한 역사를 지닌 석호촌의 도서관에 화재가 나서 계보들이 모두 불타고 말았다. 그래도 계보는 있어야 하지 않겠느냐는 마을 어르신인 대일 촌장님의 의견에 따라 석호촌 사람들은 계보 복원가 호석에게 의뢰를 맡기기로 했다.
적어도 현재를 함께 살아가는 N 명의 계보는 복원하고 싶은 호석이는 조사를 통해서 각자가 기억하는 조상들의 이름들을 구해냈다. 다행히도 석호촌의 맑은 정기 덕분에 기억력이 굉장히 좋은 주민들은 모든 조상을 완벽하게 기억하고 있다. 또한, 각 가문은 한 명의 시조를 root로 하는 트리 형태를 띈다는 것도 알아냈다. 이 때 "조상"이란, "자신의 부모"와 "부모의 조상"을 모두 합친 것을 의미한다.
이를 기반으로 몇 개의 가문이 존재했는 지, 각 가문에 대한 정보를 출력하는 프로그램을 작성해서 호석이를 도와주자!
입력
첫번째 줄에 석호촌에 살고 있는 사람의 수 N 이 주어진다. 두번째 줄에는 현재 살고 있는 사람들의 이름이 차례대로 주어진다. 모든 이름은 길이 1 이상 6 이하의 알파벳 소문자로 이뤄지며, 중복된 이름은 존재하지 않는다.
세번째 줄에는 기억하는 정보의 개수 M 이 주어진다. 이어지는 M개의 줄에는 "X Y" 꼴로 기억들이 주어지는데, 이는 곧 X의 조상 중에 Y가 있다는 것을 의미하며 같은 정보가 중복되어 주어지지 않는다. 입력에 모순이 있는 경우는 주어지지 않는다.
출력
첫번째 줄에는 가문의 개수 K 를 출력하라. 두 번째 줄에는 각 가문의 시조들의 이름을 공백으로 구분하여 사전순으로 출력하라.
세번째 줄부터는 이름의 사전순 대로 사람의 이름과 자식의 수, 그리고 사전순으로 자식들의 이름을 공백으로 구분하여 출력하라.
출력
- 1 ≤ N ≤ 1,000
- 0 ≤ M ≤ N×(N-1)/2
코드
from collections import defaultdict, deque import sys input = sys.stdin.readline n = int(input()) people = list(input().split()) m = int(input()) graph = defaultdict(list) # 후손 리스트 저장 indegree = defaultdict(int) # 진입차수 for _ in range(m): x, y = input().split() # x의 조상 y indegree[x] += 1 graph[y].append(x) # y의 후손 x # 진입차수가 0인 노드 = 시조 q = deque() for p in people: if indegree[p] == 0: q.append(p) # 시조 출력 print(len(q)) print(*sorted(q)) family = {i: [] for i in people} # 직접적인 부모-자식 관계 # 위상정렬 while q: now = q.popleft() for child in graph[now]: indegree[child] -= 1 if indegree[child] == 0: family[now].append(child) q.append(child) # 부모-자식 관계 출력 for parent, child in sorted(family.items()): print(parent, len(child), *sorted(child))
위상정렬을 이용하여 풀었다.
처음에는 union-find를 이용하여 MST로 족보를 만드려고 했으나 시조는 잘 찾았지만 부모-자식 관계를 찾는것이 너무 복잡하여 위상정렬을 이용하기로 했다. 조상->후손으로 방향이 있는 그래프이기 때문에 위상정렬을 사용할 수 있다.
indegree에는 해당 노드의 진입차수, 즉 조상의 수를 저장한다.
graph는 딕셔너리로, key인 사람의 후손들이 value로 저장된다.
모든 가족관계 정보가 주어지고 난 뒤 진입 차수가 0인 노드들이 바로 시조이다. 시조들을 q에 넣어주고 위상정렬을 수행한다.
- 이미 살펴본 현재 노드(now)와 자식 노드들 간의 관계를 끊어주어야 한다. 그러기 위해서 자식 노드의 진입차수를 1 감소시킨다. 이때 진입차수가 0이 되는 노드는 현재 노드의 직접적인 자식이므로 q에 넣어주고, 직접적인 부모-자식 관계를 기록하는 family[now]도 넣어준다.
'문제풀이 > 위상정렬' 카테고리의 다른 글
[Python/파이썬] 백준 1516번 게임 개발 (0) | 2025.01.29 |
---|---|
[Python/파이썬] 백준 9470번 Strahler 순서 (1) | 2023.12.07 |
[Python/파이썬] 백준 14676번 영우는 사기꾼? (0) | 2023.05.18 |
[Python/파이썬] 백준 2056번 작업 (0) | 2023.05.17 |
[Python/파이썬] 백준 1766번 문제집 (0) | 2023.04.01 |