이 문제는 유니온-파인드 집합 문제입니다.
DFS나 BFS로 풀 때, 유의해야 할 점은 결국 자기자신으로 돌아오는 여행루트도 존재한다는 점입니다.
가장 직관적이게 풀 수 있는 알고리즘은 union-find 인데요, 여행계획이 어떻게 주어지더라도 결국 여행이 가능하려면, 각 도시는 같은 사이클에 존재해야 한다는 전제가 있어야 합니다.
하나의 사이클에 같이 존재하지 않는다면, 다시말해 어떠한 도시에서 어떤 도시로 갈 수 있는 방법이 존재하지 않는다면, 여행은 불가능하겠죠.
풀이는 코드를 토대로 설명드리겠습니다.
import sys
input = sys.stdin.readline
def find_parent(parent,x):
if parent[x] != x:
return find_parent(parent,parent[x])
return parent[x]
def union_parent(parent,a,b):
a = find_parent(parent,a)
b = find_parent(parent,b)
if a < b :
parent[b] = a
else:
parent[a] = b
N = int(input()) # 도시의 수 N
M = int(input()) # 여행계혹에 속한 도시들의 수 M
graph = [[] for _ in range(N+1)] # 도시 번호는 1부터니까, 0번째에는 빈 리스트로 초기화
parent = [k for k in range(N+1)] # 0부터 N까지의 도시의 부모를 자기 자신으로 초기화
# print(parent)
for i in range(1,N+1):
row = list(map(int,input().strip().split()))
graph[i].append(row)
for idx,j in enumerate(row):
if j == 1: # 연결되어 있다면
# i와 idx+1 도시 집합 합치기
union_parent(parent,i,idx+1) # idx+1(도시번호는 1부터이므로 각 도시번호에 +1 을 해줘야 한다.i는 이미 해당작업 처리)
plan = list(map(int,input().strip().split()))
cycle = True # 같은 집합(같은 사이클)인지 판별하는 변수
for idx, city in enumerate(plan[:-1]):
# 여행계획의 나라의 부모가 같지않으면,(다른집합이라면)
if (find_parent(parent,city) != find_parent(parent,plan[idx+1])):
cycle = False # 다른집합이다.
if cycle == True: # 같은집합이면 여행가능
print("YES")
else:
print("NO")
먼저, 도시끼리의 연결정보를 나타내는 graph 리스트와 각 도시의 최종 부모(루트)는 누구인지 나타내는 parent 리스트가 필요합니다.
이 때, 도시는 1번 도시부터인데, 0번째 인덱스를 주의해서 설정해야 합니다. 또, 각 도시의 부모(루트)를 우선 자기자신으로 설정해야 하는 작업도 필요합니다.
각 도시마다 0과 1이 포함된 연결정보가 담긴 row를 입력받고, graph에 알맞게 추가합니다. 이 때, row를 돌면서 j 가 1 이라면, (즉 연결된 상태라는 것을 의미하는 1의 값을 만난다면) 해당 도시 i와 row에 담긴 idx+1 도시의 루트를 합쳐줍니다.
서로 연결되어 있으니까 루트는 같으니까요!
여기서 대체 루트가 뭔데? 하실 수 있으실겁니다! 루트는 말 그대로 어떠한 노드끼리 연결되었을 때 가장 작은 번호를 나타내는 노드입니다.
예를 들어, 노드 1,노드 2, 노드 3( 뭐, 여기서는 도시겠죠..?) 이 연결되어 있다면, 이 사이클(집합)의 루트노드는 노드1이 될 것입니다.
이런 작업이 union_parent함수에 구현되어 있습니다.
def find_parent(parent,x):
if parent[x] != x:
return find_parent(parent,parent[x])
return parent[x]
def union_parent(parent,a,b):
a = find_parent(parent,a)
b = find_parent(parent,b)
if a < b :
parent[b] = a
else:
parent[a] = b
union_parent함수를 살펴보면, find_parent함수를 먼저 살펴봐야 할 것입니다.
find_parent함수는 말그대로 부모(루트)를 찾는 로직입니다. 재귀를 통해서 그 사이클의 부모를 계속해서 찾아나가는 과정이 되겠죠.
다시 union_parent 함수로 돌아온다면, a노드와 b노드의 부모를 찾은 후에, a노드의 부모가 b노드의 부모보다 더 작은 수라면, b노드의 부모를 a노드의 부모로 갱신합니다.(더 작은 수가 부모노드로 설정되도록 하기 위한 작업)
반대라면, a노드의 부모를 b노드의 부모로 갱신하겠죠?
자, 거의 다 왔습니다.
이제 입력받은 여행계획을 순회하면서, 그 다음으로 여행할 도시의 부모(루트)와 지금 순회하고 있는 도시의 부모(루트)가 다르다면, 두 도시는 서로 다른 집합에 있는 것입니다. 즉, 여행할 수 없는 계획이라는 것이죠. cycle이 False가 된다면 남은 여행계획을 볼 필요 없이 바로 break해서 나와도 됩니다. (위 코드에서는 break는 안했습니다!)
여행이 가능하다면, YES를 출력해주고
두 도시가 "서로 다른 집합, 즉 다른 사이클에 있으면, 다시말해 부모(루트)가 다른 도시라면!!", 여행할 수 없는 계획이기 때문에, NO를 출력해주면 됩니다!
플로이드 워셜 알고리즘으로도 풀 수 있지만, 유니온-파인드 알고리즘이 조금 더 시간복잡도가 낮기 때문에 이런 로직으로 정리해봤습니다.
'컴퓨터 공부 > 📚 Baekjoon(백준)' 카테고리의 다른 글
[백준/알고리즘/python/java] 2852번 - NBA 농구 (0) | 2023.11.16 |
---|---|
[백준/알고리즘/python/java] 14499번 - 주사위 굴리기 (2) | 2022.12.12 |
[백준/알고리즘/python/java] 2309번 - 일곱 난쟁이 (0) | 2022.09.27 |
[백준/알고리즘/python/java] 11723번 - 집합 (0) | 2022.06.28 |
[백준/알고리즘/python/java] 2212번 - 센서 (0) | 2022.03.26 |