최단 경로
최단경로 알고리즘은 말 그대로 가장 짧은 경로를 찾는 알고리즘이다.
그래서 '길찾기' 문제라고도 불린다.
최단 경로 알고리즘 유형에는 다양한 종류가 있는데. 상황에 맞는 효율적인 알고리즘이 이미 정립되어 있다.
예를 들어 '한 지점에서 다른 특정 지점까지의 최단 경로를 구해야 하는 경우', '모든 지점에서 다른 모든 지점까지의 최단 경로를 모두 구해야 하는 경우' 등의 다양한 사례가 존재한다. 이런 사례에 맞는 알고리즘을 알고 있다면 문제를 좀 더 쉽게 풀 수 있다.
최단 경로 문제는 보통 그래프를 이용해 표현하는데 각 지점은 그래프에서 '노드'로 표현되고, 지점간 연결된 도로는 그래프에서 '간선'으로 표현된다. 또한 코딩테스트에서는 최단 경로를 모두 출력하는 문제보다는 단순히 최단 거리를 출력하도고 요구하는 문제가 더 많다.
대표적인 최단 거리 알고리즘으로는 다익스트라 알고리즘, 플로이드 워셜, 벨만 포드 알고리즘 이 3가지가 있다.
또한, 그리디 알고리즘과 다이나믹 프로그래밍 알고리즘이 최단 경로 알고리즘에 그대로 적용된다는 특징이 있다. 사실 최단 경로는 그리디 알고리즘 및 다이나믹 프로그래밍 알고리즘의 유형이다.
다익스트라 최단 경로 알고리즘
다익스트라 최단 경로 알고리즘은 그래프에서 여러개의 노드가 있을때, 특정한 노드에서 출발하여 다른 노드로 가는 각각의 최단 경로를 구해주는 알고리즘이다. 다익스트라 최단 경로 알고리즘은 '음의 간선'이 없을 때 정상적으로 작동한다. 음의 간선이란 0보다 작은 값을 가지는 간선을 의미하는 데, 현실 세계의 길은 음의 간선으로 표현되지 않으므로 다익스트라 알고리즘은 실제 gps소프트웨어의 기본 알고리즘으로 채택된다.
다익스트라 최단 경로 알고리즘은 기본적으로 그리디 알고리즘으로 분류된다. 매번 '가장 비용이 적은 노드'를 선택하여 임의의 과정을 반복하기 때문이다. 알고리즘의 원리를 간력히 설명하면 다음과 같다.
1) 출발 노드를 정한다.
2) 최단 거리 테이블을 초기화한다.
3) 방문하지 않은 노드 중에서 최단 거리가 가장 짧은 노드를 선택한다.
4) 해당 노드를 거쳐 다른 노드로 가는 비용을 계산하여 최단 거리 테이블을 갱신한다.
4) 위의 과정에서 3)과 4)를 반복한다.
다익스트라 알고리즘은 최단 경로를 구하는 과정에서 '각 노드에 대한 현재까지의 최단 거리' 정보를 항상 1차원 리스트에 저장하며 리스트를 계속 갱신하는 특징이 있다. 매번 현재 처리하고 있는 노드를 기준으로 주변 간선을 확인한다. 나중에 현재 처리하고 있는 노드와 인접한 노드로 도달하는 더 짧은 경로를 찾으면 '더 짧은 경로도 있었네? 이제부터는 이경로가 제일 짧은 경로야'라고 판단하는 것이다. 따라서 '방문하지 않은 노드중에서 현재 최단 거리가 가장 짧은 노드를 확인' 해 그 노드에 4)번 과정을 수행한다는 점에서 그리디 알고리즘으로 볼 수 있다.
다익스트라 알고리즘은 구현하는 방법이 2가지 이며
방법1, 구현하기 쉽지만 느리게 동작하는 코드
방법2. 구현하기 조금더 까다롭지만 빠르게 동작하는 코드
시험을 준비하는 입장에서는 방법 2를 정확히 이해아소 구현 할 수 있을 때까지 연습해야한다.
특히, 알고리즘 대회를 준비하는 입장에서는 다익스트라 알고리즘은 자다가도 일어나서 바로 코드를 작성할 줄 알아야한다.
방법 1) 간단하게 구현하기
import sys
input = sys.stdin.readline
INF = int(1e9)
n,m = map(int,input().split())
start = int(input())
graph = [[] for i in range(n+1)]
visited = [False] * (n+1)
distance= [INF] * (n+1)
for _ in range(m):
a, b, c = map(int, input().split())
graph[a].append((b,c))
def get_smallest_node():
min_value = INF
index = 0
for i in range(1, n+1):
if distance[i] <min_value and not visited[i]:
min_value = distance[i]
index = i
return index
def dijkstra(start):
distance[start] = 0
visited[start] = True
for j in graph[start]:
distance[j[0]] = j[1]
for i in range(n-1):
now = get_smallest_node()
visited[now] = True
for j in graph[now]:
cost = distance[now] + j[1]
if cost < distance[j[0]]:
distance[j[0]] = cost
dijkstra(start)
for i in range(1,n+1):
if distance[i] == INF:
print("INF")
else:
print(distance[i])
"""
6 11
1
1 2 2
1 3 5
1 4 1
2 3 3
2 4 2
3 2 3
3 6 5
4 3 3
4 5 1
5 3 1
5 6 2
"""
방법 2) 힙 사용 경우
import sys
import heapq
input = sys.stdin.readline
INF = int(1e9)
n,m = map(int,input().split())
start = int(input())
graph = [[] for i in range(n+1)]
distance= [INF] * (n+1)
for _ in range(m):
a, b, c = map(int, input().split())
graph[a].append((b,c))
def dijkstra(start):
q = []
heapq.heappush(q, (0,start))
distance[start] = 0
while q:
dist, now = heapq.heappop(q)
if distance[now] <dist:
continue
for i in graph[now]:
cost = dist + i[1]
if cost < distance[i[0]]:
distance[i[0]] = cost
heapq.heappush(q,(cost,i[0]))
dijkstra(start)
for i in range(1,n+1):
if distance[i] == INF:
print("INF")
else:
print(distance[i])
<출처> 이것이 코딩 테스트다. 저자 나동빈
'알고리즘 공부 > 다익스트라' 카테고리의 다른 글
[백준] 1238번 파티 - 파이썬 (0) | 2022.04.14 |
---|---|
[백준] 1916번 최소비용 구하기 - 파이썬 (0) | 2022.04.12 |
[백준] 1753번 최단경로 - 파이썬 (0) | 2022.03.28 |