참고자료 : 이것이 μ½”λ”© ν…ŒμŠ€νŠΈλ‹€ (λ‚˜λ™λΉˆ μ €)

1. λ‹€μ–‘ν•œ κ·Έλž˜ν”„ μ•Œκ³ λ¦¬μ¦˜

\ κ·Έλž˜ν”„ 트리
λ°©ν–₯μ„± λ°©ν–₯ κ·Έλž˜ν”„ ν˜Ήμ€ 무방ν–₯ κ·Έλž˜ν”„ λ°©ν–₯ κ·Έλž˜ν”„
μˆœν™˜μ„± μˆœν™˜ 및 λΉ„μˆœν™˜ λΉ„μˆœν™˜
루트 λ…Έλ“œ 쑴재 μ—¬λΆ€ 루트 λ…Έλ“œκ°€ μ—†μŒ 루트 λ…Έλ“œκ°€ 쑴재
λ…Έλ“œκ°„ 관계성 λΆ€λͺ¨μ™€ μžμ‹ 관계 μ—†μŒ λΆ€λͺ¨μ™€ μžμ‹ 관계
λͺ¨λΈμ˜ μ’…λ₯˜ λ„€νŠΈμ›Œν¬ λͺ¨λΈ 계측 λͺ¨λΈ

μ„œλ‘œμ†Œ 집합 (Disjoint Sets)

  • μ„œλ‘œμ†Œ 집합 자료ꡬ쑰 : μ„œλ‘œμ†Œ λΆ€λΆ„ μ§‘ν•©λ“€λ‘œ λ‚˜λˆ„μ–΄μ§„ μ›μ†Œλ“€μ˜ 데이터λ₯Ό μ²˜λ¦¬ν•˜κΈ° μœ„ν•œ 자료ꡬ쑰
  • 합집합과 μ°ΎκΈ° μ—°μ‚°μœΌλ‘œ κ΅¬μ„±λœλ‹€. => union-find 자료ꡬ쑰
  • μ„œλ‘œμ†Œ 집합 자료ꡬ쑰λ₯Ό κ΅¬ν˜„ν•  λ•ŒλŠ” 트리 자료ꡬ쑰 이용
  1. 합집합 μ—°μ‚° -> μ„œλ‘œ μ—°κ²°λœ 두 λ…Έλ“œ A, B 확인
    1. A와 B의 루트 λ…Έλ“œ A’, B’λ₯Ό 찾음
    2. A’λ₯Ό Bβ€™μ˜ λΆ€λͺ¨ λ…Έλ“œλ‘œ μ„€μ •
  2. λͺ¨λ“  합집합 연산을 μ²˜λ¦¬ν•  λ•ŒκΉŒμ§€ 1번 반볡

기본적인 μ„œλ‘œμ†Œ 집합 μ•Œκ³ λ¦¬μ¦˜ μ†ŒμŠ€μ½”λ“œ

def find_parent(parent, x):
    if parent[x] != x:
        return find_parent(parent, parent[x])
    return 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

v, e = map(int, input().split())
parent = [0] * (v+1)

for i in range(1, v+1):
    parent[i] = i

for i in range(e):
    a, b = map(int, input().split())
    union_parent(parent, a, b)

print('각 μ›μ†Œκ°€ μ†ν•œ 집합: ', end = ' ')
for i in range(1, v+1):
    print(find_parent(parent, i), end=' ')

# 1 1 1 1 5 5
  • 문제점 : find ν•¨μˆ˜μ˜ μ‹œκ°„λ³΅μž‘λ„λŠ” O(V)

경둜 μ••μΆ• 기법을 μ μš©ν•˜μ—¬ κ°œμ„ ν•œ μ†ŒμŠ€μ½”λ“œ

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[b] = a
    else :
        parent[a] = b

v, e = map(int, input().split())
parent = [0] * (v+1)

for i in range(1, v+1):
    parent[i] = i

for i in range(e):
    a, b = map(int, input().split())
    union_parent(parent, a, b)

print('각 μ›μ†Œκ°€ μ†ν•œ 집합: ', end = ' ')
for i in range(1, v+1):
    print(find_parent(parent, i), end=' ')

μ„œλ‘œμ†Œ 집합 μ•Œκ³ λ¦¬μ¦˜μ˜ μ‹œκ°„ λ³΅μž‘λ„

  • λ…Έλ“œμ˜ 개수 V개, μ΅œλŒ€ V-1개의 union μ—°μ‚°, M개의 find μ—°μ‚°
  • O(V+M(1+log_(2-M/V)V))

μ„œλ‘œμ†Œ 집합을 ν™œμš©ν•œ 사이클 νŒλ³„

  1. 각 간선은 ν™•μΈν•˜λ©° 두 λ…Έλ“œμ˜ 루트 λ…Έλ“œλ₯Ό ν™•μΈν•œλ‹€.
    1. 루트 λ…Έλ“œκ°€ λ‹€λ₯΄λ©΄ 두 λ…Έλ“œμ— λŒ€ν•΄ union μ—°μ‚°
    2. κ°™μœΌλ©΄ 사이클 λ°œμƒ
  2. λͺ¨λ“  간선에 λŒ€ν•΄ 1번 반볡
  • 이 μ•Œκ³ λ¦¬μ¦˜μ€ 간선에 λ°©ν–₯성이 μ—†λŠ” 무방ν–₯ κ·Έλž˜ν”„μ—μ„œλ§Œ 적용 κ°€λŠ₯ν•˜λ‹€.
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[b] = a
    else :
        parent[a] = b

v, e = map(int, input().split())
parent = [0] * (v+1)

for i in range(1, v+1):
    parent[i] = i

cycle = False

for i in range(e)
    a, b = map(int, input().split())
    if find_parent(parent, a) == find_parent(parent, b):
        cycle = True
        break
    else :
        union_parent(parent, a, b)

if cycle : print('사이클 λ°œμƒ')

μ‹ μž₯ 트리 (spanning tree)

  • μ‹ μž₯ 트리 : ν•˜λ‚˜μ˜ κ·Έλž˜ν”„κ°€ μžˆμ„ λ•Œ λͺ¨λ“  λ…Έλ“œλ₯Ό ν¬ν•¨ν•˜λ©΄μ„œ 사이클이 μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” λΆ€λΆ„ κ·Έλž˜ν”„
  • λͺ¨λ“  λ…Έλ“œκ°€ ν¬ν•¨λ˜κ³  사이클이 μ‘΄μž¬ν•˜μ§€ μ•ŠμŒ -> 트리의 성립 쑰건

크루슀칼 μ•Œκ³ λ¦¬μ¦˜

  • λŒ€ν‘œμ μΈ μ΅œμ†Œ μ‹ μž₯ 트리 μ•Œκ³ λ¦¬μ¦˜
  • μ΅œμ†Œ μ‹ μž₯ 트리 μ•Œκ³ λ¦¬μ¦˜ : μ‹ μž₯ 트리 μ€‘μ—μ„œ μ΅œμ†Œ λΉ„μš©μœΌλ‘œ λ§Œλ“€ 수 μžˆλŠ” μ‹ μž₯ 트리 μ•Œκ³ λ¦¬μ¦˜
  • κ°€μž₯ 적은 λΉ„μš©μœΌλ‘œ λͺ¨λ“  λ…Έλ“œ μ—°κ²°
  • 그리디 μ•Œκ³ λ¦¬μ¦˜μœΌλ‘œ λΆ„λ₯˜
  1. κ°„μ„  데이터λ₯Ό λΉ„μš©μ— 따라 μ˜€λ¦„μ°¨μˆœ μ •λ ¬
  2. 간선을 ν•˜λ‚˜μ”© ν™•μΈν•˜λ©° ν˜„μž¬ 간선이 사이클을 λ°œμƒμ‹œν‚€λŠ”μ§€ 확인
    1. λ°œμƒν•˜μ§€ μ•ŠμœΌλ©΄ μ΅œμ†Œ μ‹ μž₯ νŠΈλ¦¬μ— 포함
    2. λ°œμƒν•˜λ©΄ ν¬ν•¨μ‹œν‚€μ§€ μ•ŠμŒ
  3. λͺ¨λ“  간선에 λŒ€ν•΄ 2번 반볡
  • μ΅œμ’…μ μΈ μ‹ μž₯ νŠΈλ¦¬μ— ν¬ν•¨λ˜λŠ” κ°„μ„ μ˜ κ°œμˆ˜λŠ” λ…Έλ“œμ˜ 개수 - 1 κ³Ό κ°™λ‹€.
  • μ‹œκ°„ λ³΅μž‘λ„ : κ°„μ„ μ˜ κ°œμˆ˜κ°€ E개일 λ•Œ, O(ElogE)

크루슀칼 μ•Œκ³ λ¦¬μ¦˜ μ½”λ“œ

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[b] = a
    else :
        parent[a] = b

v, e = map(int, input().split())
parent = [0] * (v+1)

edges = []
result = 0

for i in range(1, v+1):
    parent[i] = i

for _ in range(e):
    a, b, cost = map(int, input().split())
    edges.append((cost, a, b))

edges.sort()

for edge in edges:
    cost, a, b = edge
    if find_parent(parent, a) != find_parent(parent, b):
        union_parent(parent, a, b)
        result += cost

print(result)

μœ„μƒμ •λ ¬

  • μˆœμ„œκ°€ μ •ν•΄μ Έ μžˆλŠ” 일련의 μž‘μ—…μ„ μ°¨λ‘€λŒ€λ‘œ μˆ˜ν–‰ν•΄μ•Ό ν•  λ•Œ μ‚¬μš©ν•  수 μžˆλŠ” μ•Œκ³ λ¦¬μ¦˜
  • μœ„μƒμ •λ ¬μ΄λž€, λ°©ν–₯ κ·Έλž˜ν”„μ˜ λͺ¨λ“  λ…Έλ“œλ₯Ό λ°©ν–₯성에 거슀λ₯΄μ§€ μ•Šλ„λ‘ μˆœμ„œλŒ€λ‘œ λ‚˜μ—΄ν•˜λŠ” 것
  • μ§„μž…μ°¨μˆ˜ : νŠΉμ •ν•œ λ…Έλ“œλ‘œ λ“€μ–΄μ˜€λŠ” κ°„μ„ μ˜ 개수
  1. μ§„μž…μ°¨μˆ˜κ°€ 0인 λ…Έλ“œλ₯Ό 큐에 λ„£μŒ
  2. 큐가 빌 λ•ŒκΉŒμ§€ λ‹€μŒ κ³Όμ • 반볡
    1. νμ—μ„œ μ›μ†Œλ₯Ό κΊΌλ‚΄ ν•΄λ‹Ή λ…Έλ“œμ—μ„œ μΆœλ°œν•˜λŠ” 간선을 κ·Έλž˜ν”„μ—μ„œ 제거
    2. μƒˆλ‘­κ²Œ μ§„μž…μ°¨μˆ˜κ°€ 0이 된 λ…Έλ“œλ₯Ό 큐에 λ„£μŒ
  • λͺ¨λ“  μ›μ†Œλ₯Ό λ°©λ¬Έν•˜κΈ° 전에 큐가 λΉˆλ‹€λ©΄ 사이클이 μ‘΄μž¬ν•˜λŠ” 것
  • λ‹€λ§Œ 기본적으둜 μœ„μƒ μ •λ ¬ λ¬Έμ œμ—μ„œλŠ” 사이클이 λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€κ³  λͺ…μ‹œν•˜λŠ” κ²½μš°κ°€ 더 많음
  • μ‹œκ°„ λ³΅μž‘λ„ : O(V+E)
from collections import deque

v, e = map(int, input().split())
indegree = [0] * v+1
graph = [[] for i in range(v+1)]

for _ in range(e) :
    a, b = map(int, input().split())
    graph[a].append(b)
    indegree[b] += 1

def topology_sort():
    result = [] # μ•Œκ³ λ¦¬μ¦˜ μˆ˜ν–‰ κ²°κ³Ό
    q = deque()

    for i in range(1, v+1) :
        if indegree[i] == 0 : q.append(i)

    while q :
        now = q.popleft()
        result.append(now)
        for i in graph[now]:
            indegree[i] -= 1
            if indegree[i] == 0 :
                q.append(i)

for i in result:
    print(i, end=' ')

topology_sort()