


BOJ에서 정답이 여러가지인 경우에는 스페셜 저지를 사용한다. 스페셜 저지는 유저가 출력한 답을 검증하는 코드를 통해서 정답 유무를 결정하는 방식이다. 오늘은 스페셜 저지 코드를 하나 만들어보려고 한다.
정점의 개수가 N이고, 정점에 1부터 N까지 번호가 매겨져있는 양방향 그래프가 있을 때, BFS 알고리즘은 다음과 같은 형태로 이루어져 있다.
2-2 단계에서 방문하지 않은 정점을 방문하는 순서는 중요하지 않다. 따라서, BFS의 결과는 여러가지가 나올 수 있다.
트리가 주어졌을 때, 올바른 BFS 방문 순서인지 구해보자.
첫째 줄에 정점의 수 N(2 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N-1개의 줄에는 트리의 간선 정보가 주어진다. 마지막 줄에는 BFS 방문 순서가 주어진다. BFS 방문 순서는 항상 N개의 정수로 이루어져 있으며, 1부터 N까지 자연수가 한 번씩 등장한다.
입력으로 주어진 BFS 방문 순서가 올바른 순서면 1, 아니면 0을 출력한다.
먼저 예제를 통해 문제를 살펴보자.

예제 1에서 주어진 그래프를 그려보면 이와 같이 나타낼 수 있고,
1부터 탐색을 시작하면 나올수 있는 탐색 순서는 (1 2 3 4), (1 3 2 4)로 볼 수 있다.
이해를 위해 더 복잡한 그래프를 예로 들어보자,

위와 같은 그래프를 1부터 탐색했을 때 올바른 탐색 순서는
(1 2 3 4 5 6), (1 2 3 5 4 6), (1 3 2 6 4 5), (1 3 2 6 5 4)
가 될 수 있는데 이를 보면 부모-자식 관계에 따라 순서가 정해지는 것을 볼 수 있다.
빨간색은 2와 2의 자식 노드, 파란색은 3과 3의 자식노드
(1 {2 3} {4 5 6})
(1 {2 3} {5 4 6})
(1 {3 2} {6 4 5})
(1 {3 2} {6 5 4})
이처럼 중괄호로 묶인 형제 노드들의 자식 노드들은 자신들의 순서와는 상관 없지만 그 위 부모 노드의 순서에 영향을 받는다는 것을 알 수 있다.
따라서, 먼저 각 노드들의 부모-자식 관계를 찾은 뒤, 이들을 집합으로 묶어 비교를 해주면 입력으로 주어진 순서가 올바른 순서인지 알 수 있다.
코드와 함께 살펴보자.
defaultdict(<class 'list'>, {1: [2, 3], 2: [4]}) 이와같이 출력되는 것을 확인할 수 있다.def bfs():
q = deque()
q.append(1) # BFS 1부터 시작
visited[1] = True
while q:
x = q.popleft()
for next in graph[x]:
if not visited[next]:
visited[next] = True
children[x].append(next) # 딕셔너리에 부모-자식 관계 저장
q.append(next)
b = ans[idx:idx + len(a)]q = deque()
q.append(1) # BFS 1부터 시작
idx = 1 # ans[0] 헀으니까 1부터 시작
while q:
x = q.popleft()
a = set(children[x]) # a에 x의 자식노드 저장
b = ans[idx:idx + len(a)] # 그 depth만큼 슬라이싱 -> x의 자식노드 자르기
q.extend(b) # 다음 탐색할 노드로 저장
b = set(b) # 비교 위해서 set으로 변환
idx += len(a) # 다음 자식노드 탐색하기 위해 idx 늘려주기
if a != b: # 둘의 집합이 다르다면
print(0)
break
else:
print(1)
import sys
from collections import deque, defaultdict
# input = sys.stdin.readline
def bfs():
q = deque()
q.append(1) # BFS 1부터 시작
visited[1] = True
while q:
x = q.popleft()
for next in graph[x]:
if not visited[next]:
visited[next] = True
children[x].append(next) # 딕셔너리에 부모-자식 관계 저장
q.append(next)
n = int(input())
graph = [[] for _ in range(n + 1)]
visited = [False for _ in range(n + 1)]
for _ in range(n - 1):
a, b = map(int, input().split())
graph[a].append(b)
graph[b].append(a)
ans = list(map(int, input().split()))
children = defaultdict(list)
bfs()
# print(children) # defaultdict(<class 'list'>, {1: [2, 3], 2: [4]})
if ans[0] != 1:
print(0)
else:
q = deque()
q.append(1) # 1 = ans[0]
idx = 1 # ans[0] 헀으니까 1부터 시작
while q:
x = q.popleft()
a = set(children[x]) # a에 x의 자식노드 저장
b = ans[idx:idx + len(a)] # 그 depth만큼 슬라이싱 -> x의 자식노드 자르기
q.extend(b) # 다음 탐색할 노드로 저장
b = set(b) # 비교 위해서 set으로 변환
idx += len(a) # 다음 자식노드 탐색하기 위해 idx 늘려주기
if a != b: # 둘의 집합이 다르다면
print(0)
break
else:
print(1)
BFS를 사용하여 탐색만 할 줄 알았지 탐색이 완료된 순서가 참인지 찾아내는 것은 매우 까다로웠다. 또한 Python의 defaultdict 자료형 또한 알게 되었다.
https://www.acmicpc.net/problem/16940