알고리즘

최소 신장 트리 - Prim's algorithm

담쏙 2021. 10. 16. 22:00
728x90

프림 알고리즘 (Prim's Algorithm)

시작 정점을 선택한 후, 정점에 연결된 간선 중 최소 비용으로 연결된 정점을 선택한다. 또 간선으로 연결된 정점들 중에서 최소 간선으로 연결된 정점을 하나씩 선택해가며 최소 신장 트리(Minimal Spannin Tree)를 만들어가는 알고리즘.

 

A를 시작 정점으로 선택하면 정점에 5, 6, 10 의 비용을 가진 간선들 중 하나를 선택할 수 있다. 이 중 최소비용을 가진 5의 간선을 선택하면, F와 연결된 7 비용의 간선과 남은 6, 10 의 비용을 가진 간선 중 하나를 선택할 수 있다.

7, 6, 10 중 6의 간선 비용이 가장 최소 비용이므로 A와 C를 연결 해준다. 또 C를 기준으로 11, 3, 30 의 간선을 갈 수 있게 되며 이전에 남은 간선들과 새로운 간선들 중 3의 비용을 가진 간선이 가장 최소이므로 또 연결 해준다.

남은 간선들을 기준으로 최소 비용 간선을 찾으면 E-F 간선이나, 이는 cycle을 형성하므로 그다음으로 최소 비용이 드는 E-B 간선을 선택한다. cycle이 형성되는 간선을 모두 제외하고 나면 아래와 같이 최소 신장 트리가 완성 된다.

 

이는 우선 순위 큐(heapq)를 이용하거나, 거리 정보를 저장하는 리스트를 이용하여 구현할 수 있다.

 

거리 정보 리스트를 이용한 반복문 구현 

거리 정보 저장을 인접 행렬 혹은 인접 리스트로 구현할 수 있는데, 인접 리스트의 경우 append 연산을 사용하기 때문에 데이터 저장 시 시간이 오래 걸리므로 인접 행렬을 이용하는 것이 좋다.

def prim(start, adj):
    INF = 0xffffffffff
    dist = adj[start][:]    # 시작 점을 기준으로 거리 저장
    ans = 0
    team = [0]*V            # 이미 선택된 정점인지 확인 (cycle 방지)
    for _ in range(V):
        min_d = INF
        min_v = 0
        for i in range(V):  # 모든 정점을 돌며 최소 비용 간선(정점) 정보를 저장
            if dist[i] < min_d and not team[i]:
                min_d = dist[i]
                min_v = i

        team[min_v] = 1     # 최소 비용 간선 선택
        ans += min_d        # 최소 비용에 추가

        for i in range(V):  # 추가된 정점을 기준으로 거리 갱신
            if dist[i] > adj[min_v][i] and not team[i]:
                dist[i] = adj[min_v][i]

 

우선 순위 큐 (힙 큐) 를 이용한 구현

이번에도 간선 정보 저장은 인접행렬을 이용하였다.

import heapq

def prin(start):
    INF = 0xffffffff
    q = []
    heapq.heappush(q, (start, 0))     #시작점부터 heapq에 저장. 비용을 기준으로 정렬되도록 함
    ans = 0
    team = [0]*V
    while q:                          # q가 빌 때까지 (모든 정점 확인)
        w, node = heapq.heappop(q)    # 가장 비용이 적은 간선과 정점 pop
        if team[node]:                # 만약 이미 연결되어 있다면 continue
            continue
        team[node] = 1                # 간선 연결
        ans += w
        for i in range(V):            # 방금 연결한 정점과 이어진 간선들 heapq에 push
            if not team[i] and adj[node][i] < INF:
                heapq.heappush(q, (adj[node][i], i))

 

heapq가 좀더 직관적이지만, pop, push 등의 함수 호출 때문에 시간이 훨씬 더 많이 걸린다. 노드의 수가 많다면 반복문을 이용하는 것이 좋다.

'알고리즘' 카테고리의 다른 글

[SWEA] 1249. 보급로  (0) 2021.10.17
[백준] 15686. 치킨 배달  (0) 2021.10.17
[SWEA] 7465. 창용 마을 무리의 개수 (bfs, union-find)  (0) 2021.10.15
[SWEA] 2814. 최장 경로  (0) 2021.10.14
[SWEA] 5656. 벽돌 깨기  (0) 2021.10.12