1647 - 도시 분할 계획
📌 문제
동물원에서 막 탈출한 원숭이 한 마리가 세상구경을 하고 있다. 그러다가 평화로운 마을에 가게 되었는데, 그곳에서는 알 수 없는 일이 벌어지고 있었다.
마을은 N개의 집과 그 집들을 연결하는 M개의 길로 이루어져 있다. 길은 어느 방향으로든지 다닐 수 있는 편리한 길이다. 그리고 각 길마다 길을 유지하는데 드는 유지비가 있다.
마을의 이장은 마을을 두 개의 분리된 마을로 분할할 계획을 가지고 있다. 마을이 너무 커서 혼자서는 관리할 수 없기 때문이다. 마을을 분할할 때는 각 분리된 마을 안에 집들이 서로 연결되도록 분할해야 한다. 각 분리된 마을 안에 있는 임의의 두 집 사이에 경로가 항상 존재해야 한다는 뜻이다. 마을에는 집이 하나 이상 있어야 한다.
그렇게 마을의 이장은 계획을 세우다가 마을 안에 길이 너무 많다는 생각을 하게 되었다. 일단 분리된 두 마을 사이에 있는 길들은 필요가 없으므로 없앨 수 있다. 그리고 각 분리된 마을 안에서도 임의의 두 집 사이에 경로가 항상 존재하게 하면서 길을 더 없앨 수 있다. 마을의 이장은 위 조건을 만족하도록 길들을 모두 없애고 나머지 길의 유지비의 합을 최소로 하고 싶다. 이것을 구하는 프로그램을 작성하시오.
📋 코드
def find_parent(parent, x):
if parent[x] != x:
parent[x] = 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[a] = b
else:
parent[b] = a
N, M = map(int, input().split())
graph = []
for _ in range(M):
a, b, cost = map(int, input().split())
graph.append((cost, a, b))
graph.sort()
parent = [x for x in range(N+1)]
result = []
for cost, a, b in graph:
if find_parent(parent, a) != find_parent(parent, b):
union_parent(parent, a, b)
result.append(cost)
print(sum(result[:-1]))
💡 한마디
모든 노드의 경로가 있으면서 사이클을 포함하지 않는 부분 그래프를 Spanning Tree
(신장 트리)라고 한다. 비용이 최소인 경우를 구하는 것이기 때문에 대표적인 최소 신장 트리 알고리즘인 kruskal
을 이용해서 풀었다.
유지비의 합을 최소로 하면서 마을을 두 개로 나누는 것이 목적이기 때문에 마지막 두 노드의 비용을 제외했다. 그럼 하나의 신장 트리가 두 개의 부분 그래프로 나뉘고, 가장 큰 비용을 제외함으로써 최소 비용을 만족한다.