๐๏ธ Graph Theory 5: MST with Kruskal & Prim
๐กย Spanning Tree
๊ทธ๋ํ ๋ด๋ถ์ ํฌํจ๋ ๋ชจ๋ ๋
ธ๋๋ฅผ ํฌํจํ๋ ํธ๋ฆฌ๋ฅผ ์๋ฏธํ๋ค. ๋ชจ๋ ์ ์ ์ ํฌํจํ๊ธด ํ์ง๋ง ๊ทผ๋ณธ์ ํธ๋ฆฌ๋ผ์ ์ฌ์ดํด์ด ๋ฐ์ํ๋ฉด ์๋๋ฉฐ, ์ต์์ ๊ฐ์ ์ ์ฌ์ฉํด ๋ชจ๋ ๋
ธ๋๋ฅผ ์ฐ๊ฒฐํด์ผ ํ๋ค. ๋ฐ๋ผ์ Spanning Tree
์ ๊ฐ์ ๊ฐ์๋ ๋
ธ๋ ๊ฐ์-1
์ ํด๋นํ๋ค.
๐ตย Minimum Spanning Tree
๊ทธ๋ํ ์์์ ๋ฐ์ํ ์ ์๋ ์ฌ๋ฌ Spanning Tree
์ค์์ ๊ฐ์ ๋ค์ ๊ฐ์ค์น ํฉ์ด ์ต์์ธ ํธ๋ฆฌ๋ฅผ ์๋ฏธํ๋ค. MST
๋ฅผ ๊ตฌํํ๋ ์๊ณ ๋ฆฌ์ฆ์ ๋ํ์ ์ผ๋ก Kruskal
, Prim
์๊ณ ๋ฆฌ์ฆ์ด ์๋ค. ์ ์์ ์๊ฐ ๋ณต์ก๋๋ O(ElogE)
, ํ์๋ ๊ธฐ๋ณธ์ ์ผ๋ก O(N^2)
์ด๋ผ์ ๋
ธ๋์ ๋นํด ๊ฐ์ ๊ฐ์๊ฐ ์ ์ ํฌ์ ๊ทธ๋ํ์ ๊ฒฝ์ฐ๋ Kruskal์, ๋
ธ๋์ ๋นํด ๊ฐ์ ์ด ๋ง์ ๋ฐ์ง ๊ทธ๋ํ์ ๊ฒฝ์ฐ๋ Prim์ ์ฌ์ฉํ๋๊ฒ ์๊ฐ ๋ณต์ก๋ ์ธก๋ฉด์์ ์ ๋ฆฌํ๋ค. ํํธ, Prim์ ๊ตฌํ์์ ์ ํํ๋ ์๋ฃ๊ตฌ์กฐ์ ๋ฐ๋ผ์ ์๊ฐ ๋ณต์ก๋๋ฅผ ์ต์ ํํ ์ ์๋ค. ์์ธํ ๋ด์ฉ์ ๊ฐ๋ณ ์๊ณ ๋ฆฌ์ฆ์ ๋ํ ์ค๋ช
์์ ๋ค๋ฃจ๋๋ก ํ๊ฒ ๋ค.
๐ย Kruskal Algorithm (๊ฐ์ ์ ํ)
๊ทธ๋ฆฌ๋ํ๊ฒ ๊ทธ๋ํ์ ๋ชจ๋ ์ ์ ์ ์ต์ ๋น์ฉ์ผ๋ก ์ฐ๊ฒฐํ๋ ๋ฐฉ๋ฒ์ด๋ค. ๊ตฌ์ฒด์ ์ผ๋ก๋ ๊ฐ๋ณ ์์ ์์ ์ฌ์ดํด์ ์ด๋ฃจ์ง ์์ผ๋ฉด์ ์ต์ ๋น์ฉ์ธ ๊ฐ์ ์ ๊ฒฝ๋ก๋ก ์ ํํ๋ค. ๊ทธ๋ฆฌ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๊ธฐ ๋๋ฌธ์ ์ด์ ๊ฒฐ๊ณผ, ๋ฏธ๋ ๊ฒฐ๊ณผ๋ฅผ ๊ณ ๋ คํ์ง ์๊ณ ํ์ฌ ์ต์ ๋น์ฉ์ด ๋๋ ๊ฐ์ ๋ง์ ์ ํํ๋ค. ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌ์ฒด์ ์ธ ๋์ ๊ณผ์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
- 1) ๊ทธ๋ํ์ ๊ฐ์ ๋ค์ ์ค๋ฆ์ฐจ์์ผ๋ก ์ ๋ ฌ, ๊ฐ์ค์น ๊ธฐ์ค
- 2) ์ฌ์ดํด์ ๋ฐ์์ํค๋์ง ์ฌ๋ถ๋ฅผ ์ฒดํฌํ๋ฉด์ ์์๋๋ก ์ ํ
- ๊ฐ์ฅ ๋ฎ์ ๊ฐ์ค์น๋ถํฐ ์ ๊ทผํด ์ฒดํฌ
- ์ฌ์ดํด ๋ฐ์ X๋ผ๋ฉด ์ ํ
- 3) ์ ํํ ๊ฐ์ ์
MST
์งํฉ์ ์ถ๊ฐ
์ฌ์ดํด์ ๋ฐ์์ํค๋์ง ์ฌ๋ถ๋ฅผ ์ฒดํฌํ๋ ๋ถ๋ถ์ด ๊ตฌํํ ๋ ๊น๋ค๋ก์ธ ์ ์๋๋ฐ, Union-Find ์๊ณ ๋ฆฌ์ฆ์ ๋์ ํ๋ฉด ์์ํ๊ฒ ๋ง๋ค์ด ๋ผ ์ ์๋ค. Union-Find๋ฅผ ๋์ ํ Kruskal Algorithm์ Python ์ฝ๋๋ก ์์ฑํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
""" kruskal algorithm example: baekjoon 1043 """
import sys
def find(arr: list, x: int) -> int:
""" method for finding root node """
if arr[x] != x:
arr[x] = find(arr, arr[x])
return arr[x]
def union(arr: list, x: int, y: int):
""" method for union-find """
x = find(arr, x)
y = find(arr, y)
if x < y:
arr[y] = x
else:
arr[x] = y
N = int(sys.stdin.readline()) # number of nodes
M = int(sys.stdin.readline()) # number of edges
graph, parent = [], [0]*(N+1)
# 0-0) ๊ฐ์ ์ฐ๊ฒฐ ์ ๋ณด ์ด๊ธฐํ, ์ ๋ ฌ
for _ in range(M):
src, end, cost = map(int, sys.stdin.readline().split())
graph.append((cost, src, end))
graph.sort()
# 0-1) ์ฐ๊ฒฐ ์ ๋ณด ์ด๊ธฐํ
for i in range(1, N+1):
parent[i] = i
# 1) Kruskal Algorithm
result = 0
for j in range(M):
weight, start, final = graph[j]
if find(parent, start) != find(parent, final):
union(parent, start, final)
result += weight
print(result)
find ๋ฉ์๋๋ ์
๋ ฅํ ๋
ธ๋์ ๋ฃจํธ ๋
ธ๋๋ฅผ ์ฐพ์ ๋ฐํํ๋ค. ์ด๊ฒ์ ํ์ฉํด ์๋ก ๋ค๋ฅธ ๋
ธ๋๊ฐ ๊ฐ์ ์งํฉ(ํธ๋ฆฌ)์ ์ํ๋์ง ์์ฝ๊ฒ ํ์ ํ ์ ์์ผ๋ฉฐ ์ด๊ฒ์ ๋ฐ๊ฟ ์๊ฐํด๋ณด๋ฉด ์ ํ๋ ๋ ๋
ธ๋๊ฐ ์ฌ์ดํด์ ๋ฐ์์ํค๋์ง ์ฌ๋ถ๋ฅผ ์์ ๋ผ ์ ์๋ค๋ ๊ฒ์ด๋ค. ๋ง์ฝ ๋ ๋
ธ๋๊ฐ ๊ฐ์ ๋ฃจํธ ๋
ธ๋๊ฐ์ ๊ฐ๋๋ค๋ฉด, ๊ฒฐ๊ตญ ๊ฐ์ ์งํฉ(ํธ๋ฆฌ)์ ์ํ๋ค๋ ๊ฒ์ ์๋ฏธํ๋ฉฐ, ์ด๊ฒ์ ์ฌ์ดํด์ ์ ๋ฐํ๊ฒ ๋๋ ๊ฒ์ด๋ค. ๋ฐ๋ผ์ ์ฌ์ดํด์ ์ ๋ฐํ๋ ์ ์ ์ ์ ํํ์ง ์์ผ๋ฉฐ, ํธ๋ฆฌ์ ์ฑ์ง์ ์ ์งํ ์ ์๋ ๋
ธ๋๋ฅผ ์ ํํด union
์ฐ์ฐ์ ๋์
ํ๋ค.
๐ดย Prim Algorithm (์ ์ ์ ํ)
ํน์ ์ ์ ์์ ์์ํด์ ๊ฐ์ค์น๊ฐ ์์ ๊ฐ์ ๋ค ์์๋๋ก ํธ๋ฆฌ๋ฅผ ํ์ฅํด๋๊ฐ๋ ๋ฐฉ๋ฒ์ด๋ค. ์์์ ์ ์ง์ ํ๋ค๋ ์ ์์ ๋ค์ต์คํธ๋ผ์ ์ ์ฌํ๋ฉฐ, ๊ฐ์ ์ ์ซ์๊ฐ ๋ง์ ๋ฐ์ง ๊ทธ๋ํ ์ํฉ์์ Kruskal
๋ณด๋ค ๋น ๋ฅด๋ค. ๊ตฌ์ฒด์ ์ธ ๋์ ๋ฐฉ์์ ๋ค์๊ณผ ๊ฐ๋ค.
- 1) ์ ํ ๋ ธ๋๋ฅผ MST ์งํฉ์ ์ถ๊ฐ
- 2) MST ์งํฉ์ ํฌํจ๋ ๋
ธ๋๋ค์ ์ธ์ ํ ์ ์ ๋ค ํ์
- ์ฌ์ดํด ๋ฐ์ ์ฌ๋ถ ํ์ธ
- ์ฌ์ดํด ๋ฐ์ X: ์ต์ ๊ฐ์ค์น์ ๊ฐ์ ์ ์ ํ
- ์ฌ์ดํด ๋ฐ์ ์ฌ๋ถ ํ์ธ
- 3) ์ ์ฒด ๊ฐ์ ์ ๊ฐ์๊ฐ N-1๊ฐ๊ฐ ๋ ๋๊น์ง, 1 & 2 ๊ณผ์
Iteration
๊ธฐ๋ณธ์ ์ผ๋ก๋ O(N^2)
์ ์๊ฐ๋ณต์ก๋๋ฅผ ๊ธฐ๋กํ๋ค. ํ์ง๋ง ์๋ฃ๊ตฌ์กฐ ์ต์ ํ์ ๋ฐ๋ผ์ Kruskal
๊ณผ ๋น์ทํ ์๊ฐ๋ณต์ก๋์ธ O(ElogE)
์ ๋๋ก๊น์ง ๋ง๋ค์ด ๋ผ ์ ์๋ค. ์ต์ ํ์ ๋ ฌ๊ณผ ์ฐ์ ์์ ํ๋ฅผ ์ด์ฉํ๋ฉด ๋๋ค. ํ์ ๋ ฌ์ ์ด์ฉํด ๊ทธ๋ํ ์ฐ๊ฒฐ ์ ๋ณด๋ฅผ ๊ฐ์ค์น๋ฅผ ๊ธฐ์ค์ผ๋ก ์ค๋ฆ์ฐจ์ ์ ๋ ฌ์ ํ ๋ค์, ์ฌ์ดํด์ ๋ฐ์์ํค์ง ์๋ ์ธ์ ๋
ธ๋๋ฅผ ์ ํํ๋๋ก ๋ง๋ ๋ค.
""" prim algorithm example: baekjoon 1197 """
import sys, heapq
from typing import List
"""
[ํ์ด]
1) Prim with ์ฐ์ ์์ ํ (ํ)
- ์์์ ์ ํ, MST ์งํฉ์ ์ถ๊ฐ
- MST ์งํฉ์ ๋
ธ๋๋ค์ ์ธ์ ํ ๋ชจ๋ ์ ์ ํ์
- ์ฌ์ดํด ๋ฐ์ ์ฌ๋ถ ํ์ธ: ๋ฐฉ๋ฌธ ์ฌ๋ถ๋ก ํ์
- ์ฌ์ดํด ๋ฐ์ X: ์ต์ ๊ฐ์ค์น ๊ฐ์ ์ ํ (heapify๋ฅผ ํตํด ๊ฐ๋ณ ๋
ธ๋๋ง๋ค ๊ฐ์ ๋ค์ ๊ฐ์ค์น ๊ธฐ์ค ์ค๋ฆ์ฐจ์ ์ ๋ ฌ)
"""
def prim(grid: List[List], visit: List[bool], start: int) -> int:
visit[start] = True
tmp = grid[start] # ์ ํ๋ ๋
ธ๋์ ๋ํ ๋ชจ๋ ์ธ์ ๊ฐ์ ์ถ์ถ
heapq.heapify(tmp) # ์ด๋ฏธ ์์ฑ๋์ด ์๋ ์๋ฃ๊ตฌ์กฐ์ ๋ํด์๋ heapq.heapify๋ฅผ ์ฌ์ฉํ๋ฉด ํ ์ฑ์ง์ ๋ง์กฑํ๋๋ก ํ ์ ์๋ค
mst, total = [], 0
while tmp:
weight, u, v = heapq.heappop(tmp)
if not visit[v]: # ๋ฏธ๋ฐฉ๋ฌธ ๋
ธ๋๋ก์ ๊ฐ์ ๋ง ์ ํํ๋ ๋ฐฉ์์ผ๋ก, ์ฌ์ดํด ๋ฐ์ ์ฌ๋ถ ํ์ ํ๋ ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํ
visit[v] = True
mst.append((u, v))
total += weight
for edge in graph[v]:
if not visit[edge[2]]:
heapq.heappush(tmp, edge)
return total
def solution():
result = prim(graph, visited, 1) # ์์ ๋
ธ๋๋ฅผ ์ด๋ค ๊ฒ์ผ๋ก ์ค์ ํด๋ ์๊ด ์์
print(result)
if __name__ == "__main__":
sys.setrecursionlimit(10**6)
V, E = map(int, sys.stdin.readline().split())
graph, visited = [[] for _ in range(V+1)], [False]*(V+1)
for _ in range(E):
src, end, cost = map(int, sys.stdin.readline().split())
graph[src].append([cost, src, end])
graph[end].append([cost, end, src])
solution()
์์ ์ ์ ์ ์๋ฌด๊ฑฐ๋ ์ ํํด๋ ์๊ด์๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ์ฅ ์ฃผ๋ชฉํ ๋ถ๋ถ์ ๋
ธ๋ ์ ํ์ ์ฌ์ดํด ์ฌ๋ถ๋ฅผ ํ์ ํ๋ ๋ฐฉ๋ฒ์ ์ด๋ป๊ฒ ๊ตฌํํ๋๊ฐ์ด๋ค. ๊ฐ๋ณ ๋
ธ๋์ ๋ํ ๋ฐฉ๋ฌธ์ฌ๋ถ๋ฅผ ๊ธฐ๋กํ๋ ๋ฐฐ์ด์ ๋ฐ๋ก ์์ฑํ ๋ค, ๋ฐฉ๋ฌธ(ํด๋น ๋
ธ๋์ ์ฐ๊ฒฐ๋๋ ๊ฐ์ ์ ํ)ํ ๋ ๋ง๋ค ๋ฐฉ๋ฌธ ๊ธฐ๋ก์ ์ ์ฅํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ง์ฝ ์ด๋ค ๋
ธ๋๋ฅผ ์ ํํ์ ๋, ์ด๋ฏธ ๋ฐฉ๋ฌธํ ๋
ธ๋๋ผ๋ฉด ํด๋น ๋
ธ๋์ ์ฐ๊ฒฐํ๋ ๊ฐ์ ์ ๊ฐ์ค์น๊ฐ ํ์ฌ ๊ฐ์ฅ ์ต์์ ํด๋นํ๋๋ผ๋ ์ฌ์ดํด์ ๋ฐ์์ํค๊ธฐ ๋๋ฌธ์ ์ ํํ์ง ์๋๋ก ์ฌ์ดํด ์ฌ๋ถ๋ฅผ ํ์ ํ๊ฒ ๋ง๋ค์๋ค.
Kruskal
์ฒ๋ผ ์ ์ฒด ๋ชจ๋ ๊ฐ์ ์ ์๊ณ ์๋ ์ํ๊ฐ ์๋์๋ ์ต์ ์คํจ๋ ํธ๋ฆฌ์ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ค์ด ๋ผ ์ ์๋ ์ด์ ๋ ๋ฏธ๋ฆฌ ๊ฐ๋ณ ๋
ธ๋๋ณ ๊ฐ์ ๋ค์ ๊ฐ์ค์น ๊ธฐ์ค์ผ๋ก ์ค๋ฆ์ฐจ์ ์ ๋ ฌ ํด๋๊ธฐ ๋๋ฌธ์ด๋ค. ์ด๊ฒ ๋๋ฌธ์ ์ง์ญ ์ต์ ๋ค์ ํฉ์ด ์ ์ญ ์ต์ ์ด ๋์ด์ผ ํ๋ ๊ทธ๋ฆฌ๋์ ์ ์ฝ ์กฐ๊ฑด์ ๋ง์กฑ์ํฌ ์ ์๊ฒ ๋๋ค.
Leave a comment