CS/알고리즘
[알고리즘] 최단경로 알고리즘
삶_
2022. 7. 11. 14:59
최단경로 알고리즘
- 가장 짧은 경로를 찾는 알고리즘
- 한 지점에서 다른 한 지점까지의 최단 경로
- 한 지점에서 다른 모든 지점까지의 최단 경로
- 모든 지점에서 다른 모든지점까지의 최단 경로
- 각 지점을 그래프에서 노드로 표현
지점간 연결된 도로는 그래프에서 간선으로 표현
다익스트라 최단경로 알고리즘
- 특정한 노드에서 출발. 다른 모든 노드로 가는 최단경로 계산
- 음의 간선이 없을때(음수가 아닐시) 정상적으로 동작함.
그리디 알고리즘으로 분류됨 - 매 상황에서 가장 비용이 적은 노드를 선택해 임의의 과정을 반복
- 출발 노드를 설정
- 최단거리 테이블을 초기화함
- 방문하지 않은 노드 중에서 최단거리가 가장 짧은 노드를 선택
- 해당 노드를 거쳐 다른 노드로 가는 비용을 계산, 최단거리 테이블을 갱신
- 위 과정에서 3,4반복

- 매 상황마다 방문하지 않은 가장 비용이 작은 노드를 선택해 임의의 과정을 반복
- 단계를 거치며 한번 처리된 노드의 최단 거리는 고정됨
- 한 단계당 하나의 노드에 대한 최단 거리를 확실히 찾는 것으로 이해할 수 있음
- 다익스트라 알고리즘을 수행한 뒤에 테이블에 각 노드까지의 최단거리 정보가 저장됨
- 간단한 구현방법
- 단계마다 방문하지 않은 노드중에서 최단 거리가 가장 짧은 노드를 선택하기 위해
- 매 단계마다 1차원 테이블의 모든 원소를 확인한다
import sys
input = sys.stdin.readline
INF = int(1e9) #무한값으로 10억 설정(임의의 값)
#노드의 개수 n, 간선의 개수 m를 입력 받음
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)) #노드 정보리스트에 담기. (a번->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] #가야할 노드의 최단거리 <= 현재노드가 가야할 노드와의 거리 대입
#시작노드를 제외한 전체 n-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("INFINITY")
else:
print(distance[i]) #최단거리 출력
다익스트라 알고리즘 : 우선순위 큐
- 우선순위가 가장 높은 데이터를 가장 먼저 삭제하는 자료구조
- 별도의 우선순위를 설정해야 함
- 힙을 사용 (최대힙, 최소힙)
- 단계마다 방문하지 않은 노드 중에서 최단 거리가 가장 짧은 노드를 선택하기 위해 힙 자료구조 이용
- 다익스트라 알고리즘이 동작하는 기본 원리는 동일
- 현재 가장 가까운 노드를 저장해 놓기 위해, 힙 자료구조를 추가적으로 이용한다는 점이 다름
- 현재의 최단 거리가 가장 짧은 노드를 선택해야 하므로, 최소 힙 사용

import heapq
#오름차순 힙정렬
def heapsort(iterable):
h = [] #힙
result = [] #결과
#모든 원소를 차례대로 힙에 삽입
for value in iterable:
heapq.heappush(h, value)
#힙에 삽입된 모든 원소를 차례대로 꺼내어 담기
for i in range(len(h)):
result.append(heapq.heappop(h)) #제일 작은수부터 가져오기. 최소힙
return result
result = heapsort([1,3,5,7,9,2,4,6,8,0])
print(result)
#결과: [0,1,2,3,4,5,6,7,8,9]
#내림차순 힙정렬
def heapsort(iterable):
h = [] #힙
result = [] #결과
#모든 원소를 차례대로 힙에 삽입
for value in iterable:
heapq.heappush(h, -value) #-붙으니까 9는 -9가 되고 제일 작아져서 앞에 서게됨
#힙에 삽입된 모든 원소를 차례대로 꺼내어 담기
for i in range(len(h)):
result.append(-heapq.heappop(h)) #제일 작은수부터 가져오기. 에 - 붙여서 다시 절댓값으로 만듬
return result
result = heapsort([1,3,5,7,9,2,4,6,8,0])
print(result)
import heapq
import sys
input = sys.stdin.readline()
INF = int(1e9) #무한 값으로 10억 설정
#아래부분은 기존 다익스트라 알고리즘과 비슷함
#노드, 간선의 개수 입력받기
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())
#a번 노드 -> b번노드로 가는 간선이 c이다
graph[a].append((b,c)) #튜플 자료형 넣기
#아래부터 우선순위 큐를 이용
def dijkstra(start):
q = [] #큐 생성
#시작노드로 가기 위한 최단 거리는 0으로 설정. 큐에 튜플 삽입.
heapq.heappush(q, (0, start))
#시작노드의 최단거리 = 0
distance[start] = 0
while q: #큐가 비어있지 않을시 계속 실행
#큐에 가장 최단거리가 짧은노드에 대한 정보 꺼내기(튜플)
#튜플(최단거리=dist, 노드인덱스=now)
dist, now = heapq.heappop(q)
#현재노드가 이미 처리된 적 있으면 무시
if distance[now] < dist:
continue
#현재 노드와 연결된 다른 인접한 노드들을 확인
for i in graph[now]:
cost = dist + i[1] #cost = 현재 노드의 최단거리+다음노드 가는데 거리
#위 값이 다음노드의 최단거리보다 짧은 경우
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("INFINITY")
else:
print(distance[i])
플로이드 워셜 알고리즘
- 모든 노드에서 다른 모든노드까지의 최단경로를 모두 계산함
- 단계별로 거쳐가는 노드를 기준으로 알고리즘을 수행하되,
- 매 단계마다 방문하지 않은 노드 중에 최단 거리를 갖는 노드를 찾는 과정이 필요하지 않음
- 2차원 테이블에 최단거리정보를 저장
- 다이나믹 프로그래밍 유형에 속함
- 노드, 간선의 개수가 적을 때 주로 사용
- 각 단계마다 특정한 노드 k 를 거쳐가는 경우를 확인
- a에서 b로 가는 최단거리보다 a에서 k를 거쳐 b로 가는 거리가 더 짧은지 검사


#무한값 설정
INF = int(1e9)
n = int(input()) #노드의 개수
m = int(input()) #간선의 개수
#2차원 리스트를 만들고, 무한으로 초기화
graph = [[INF]*(n+1) for _ in range(n+1)]
#자기자신에서 자기자신으로 가는 비용은 0으로 초기화 (의미없어서)
for a in range(1, n+1):
for b in range(1, n+1):
if a==b:
graph[a][b] = 0
#간선에 대한 정보를 입력 받아, 그 값으로 초기화
for _ in range(m):
#a에서 b로 가는 비용은 c라고 설정
a,b,c = map(int, input().split())
graph[a][b] = c
#점화식에 따라 플로이드 워셜 알고리즘 수행
#k를 거칠때를 기준으로 (k가 1일때 1을 무조건 거치는 경우라던가의 예시)
for k in range(1, n+1):
for a in range(1, n+1):
for b in range(1, n+1):
graph[a][b] = min(graph[a][b], graph[a][k] + graph[k][b])
#수행된 결과 출력
for a in range(1, n+1):
for b in range(1, n+1):
#도달할 수 없는 경우, 무한으로 출력
if graph[a][b] == INF:
print("INFINITY", end=" ")
#도달할 수 있는 경우 출력
else:
print(graph[a][b], end=" ")
print()
- n개의 도시.
각 도시는 보내고자 하는 메시지를 다른 도시로 보냄 - X라는 도시 -> Y라는 도시 하려면
도시 X에서 Y로 향하는 통로가 설치되어있어야 함 - Y에서 X로 향하는 통로가 없으면 메시지 못보냄.
메시지 보낼때 일정시간 소요됨.
- C라는 도시에 위급상황 발생.
최대한 많은 도시로 메시지를 보내고자 함. - 각 도시의 번호, 통로가 설치된 정보가 주어질 때,
도시 C에서 보낸 메시지를 받게 되는 총 도시 갯수와, 도시들이 모두 메시지를 받는데 걸리는 시간을 계산하라. - 첫째줄에 도시의 개수 N, 통로의 개수 M, 메시지를 보내고자 하는 도시 C
둘째줄~ M+1번째 줄에 걸쳐서 통로에 대한 정보 X,Y,Z가 주어짐
import heapq
import sys
INF = int(1e9)
#노드 개수, 간선 개수, 시작노드 입력받기
n,m,start = map(int, sys.stdin.readline().split())
#각 노드에 연결되어 있는 노드에 대한 정보를 담는 리스트 만들기(0번 X)
graph = [[] for i in range(n+1)]
#최단거리 테이블을 모두 무한으로 초기화
distance = [INF] *(n+1)
#모든 간선 정보를 입력받기(1번부터~)
for _ in range(m):
x,y,z = map(int, sys.stdin.readline().split())
#x번 노드에서 y번 노드로 가는 비용 = z
graph[x].append((y,z))
def dijkstra(start):
q=[] #큐 초기화
#시작 노드로 가기위한 최단 거리는 0으로 설정하여 q에 삽입 = 처음엔 0이니까
#(최단거리, 시작노드)
heapq.heappush(q, (0, start))
distance[start] = 0
while q: #큐가 비어있지 않을때
#dist = 최단거리, now = 시작노드
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)
#도달할 수 있는 노드 개수
count = 0
#멀리있는 노드까지 가는데 총 걸리는 시간
#도달할 수 있는 노드 중에서, 가장 멀리있는 노드와의 최단거리
max_distance = 0
for d in distance:
#도달할 수 있는 노드인 경우
if d!= INF:
max_distance = max(max_distance, d)
#시작 노드는 제외해야하니 count -1 출력
#총 도시갯수, 총걸리는 시간
print(count-1, max_distance)