열렬히.뛰기

가중치 그래프

알고리즘: 이론 > 알고리즘 : 그래프 (심화) > 가중치 그래프

가중치 그래프란?

간선마다 비용(weight)이 붙어 있는 그래프다. 지금까지의 그래프는 "이어져 있다/아니다"만 따졌지만, 현실의 문제는 대개 "얼마나 멀리" 또는 "얼마나 비싸게" 이어져 있느냐를 묻는다.

  • 도로망 : 도시와 도시 사이의 거리
  • 네트워크 : 회선의 지연 시간
  • 작업 : 공정 사이에 걸리는 시간

간선 (u, v)의 가중치를 w(u, v)로 적는다.

표현 방법

인접 행렬. A[u][v]에 가중치를 넣는다. 간선이 없으면 \infty(코드에서는 아주 큰 수)로 둔다. 정점이 V개면 O(V^2)의 공간을 쓰므로 간선이 빽빽할 때 유리하다.

A[u][v] = \begin{cases} w(u,v) & (u,v)\text{가 간선일 때} \\ \infty & \text{아닐 때} \end{cases}

인접 리스트. 정점마다 (이웃, 가중치) 쌍을 담는다. 공간이 O(V+E)라 간선이 성길 때 유리하고, 실제 문제 풀이에서는 대개 이쪽을 쓴다.

python
# 무방향 그래프. 방향 그래프면 한쪽만 넣는다.
graph = [[] for _ in range(n + 1)]
for u, v, w in edges:
    graph[u].append((v, w))
    graph[v].append((u, w))

왜 BFS로는 안 되는가

간선의 가중치가 모두 같을 때만 BFS가 최단거리를 준다. BFS는 "간선을 몇 개 지났나"로 거리를 세기 때문이다. 가중치가 다르면 간선 수가 적은 길이 오히려 더 비쌀 수 있다.

1 \xrightarrow{\ 10\ } 3, \qquad 1 \xrightarrow{\ 1\ } 2 \xrightarrow{\ 1\ } 3

BFS는 간선 하나짜리 위쪽 길(비용 10)을 먼저 찾지만, 실제 최단거리는 아래쪽 길(비용 2)이다. 그래서 가중치 그래프에는 따로 알고리즘이 필요하다.

어떤 알고리즘으로 이어지나

문제 알고리즘 시간복잡도 음수 간선
한 점에서 모든 점까지 다익스트라 O(E\log V) 안 됨
한 점에서 모든 점까지 벨만-포드 O(VE) 됨 (음수 순환 탐지)
모든 점 쌍 사이 플로이드-워셜 O(V^3) 됨
모든 정점을 잇는 최소 비용 크루스칼 · 프림 O(E\log E) 됨

음수 간선이 있으면 다익스트라를 쓸 수 없다. 다익스트라는 "지금까지 확정된 거리는 더 줄지 않는다"를 전제로 정점을 하나씩 확정하는데, 음수 간선이 있으면 나중에 더 짧은 길이 나타날 수 있어 그 전제가 깨진다.

바로 다음 글에서 다익스트라 알고리즘을 다룬다.