본문 바로가기
알고리즘 PS (백준)/🐍 Python (파이썬)

[백준 1504] 다익스트라(Dijkstra) - 파이썬 (Python)

by 코딩하는 동현😎 2022. 10. 14.

특정한 최단 경로

문제

 

방향성이 없는 그래프가 주어진다. 세준이는 1번 정점에서 N번 정점으로 최단 거리로 이동하려고 한다. 또한 세준이는 두 가지 조건을 만족하면서 이동하는 특정한 최단 경로를 구하고 싶은데, 그것은 바로 임의로 주어진 두 정점은 반드시 통과해야 한다는 것이다.

세준이는 한번 이동했던 정점은 물론, 한번 이동했던 간선도 다시 이동할 수 있다. 하지만 반드시 최단 경로로 이동해야 한다는 사실에 주의하라. 1번 정점에서 N번 정점으로 이동할 때, 주어진 두 정점을 반드시 거치면서 최단 경로로 이동하는 프로그램을 작성하시오.

입력

첫째 줄에 정점의 개수 N과 간선의 개수 E가 주어진다. (2 ≤ N ≤ 800, 0 ≤ E ≤ 200,000) 둘째 줄부터 E개의 줄에 걸쳐서 세 개의 정수 a, b, c가 주어지는데, a번 정점에서 b번 정점까지 양방향 길이 존재하며, 그 거리가 c라는 뜻이다. (1 ≤ c ≤ 1,000) 다음 줄에는 반드시 거쳐야 하는 두 개의 서로 다른 정점 번호 v1과 v2가 주어진다. (v1 ≠ v2, v1 ≠ N, v2 ≠ 1) 임의의 두 정점 u와 v사이에는 간선이 최대 1개 존재한다.

출력

첫째 줄에 두 개의 정점을 지나는 최단 경로의 길이를 출력한다. 그러한 경로가 없을 때에는 -1을 출력한다.

예제 입력 1

4 6
1 2 3
2 3 3
3 4 1
1 3 5
2 4 5
1 4 4
2 3

예제 출력 1

7

다익스트라 알고리즘

다익스트라 알고리즘은 그래프에서 최단거리를 구하는 알고리즘으로, 가중치 그래프에서 한 정점에서 다른 정점과의 최단거리를 구하는 알고리즘 입니다.

다음과 같이 주어진 그래프를 인접리스트로 구현합니다. 한 노드에 대해 인접한 정점을 (노드번호, 거리) 튜플로 저장합니다.

 

1 -> (2,8) (3,3)

2 -> (4,4) (5,15)

3 -> (4,13)

4 -> (5,2)

5

위와 같이 인접리스트를 만든후, 최단 거리리스트를 초기화하고 탐색을 시작합니다.

정점 1부터 출발한다고 가정하겠습니다. (INF는 무한대 입니다.)

1 2 3 4 5
0 INF INF INF INF

그 중 거리가 가장 짧은 1을 채택해서 탐색합니다.


1 2 3 4 5
0 8 3 INF INF

1은 이제 탐색했으므로 방문여부 체크하고 다시 탐색하지 않습니다. 1을 제외한 노드들 중 거리가 짧은 3을 우선적으로 탐색합니다.


1 2 3 4 5
0 8 3 3+13 INF

3을 거쳐서 4를 가는 것이므로 13을 더해줍니다.

남아 있는 노드들 중 2가 거리가 작으므로 2를 탐색합니다.


1 2 3 4 5
0 8 3 16 / 8+4 = 12 23

2를 탐색했을때 (4,4)이므로 1->2->4 를 거치게 되면 8+4= 12가 됩니다.

그러나 기존에 16이란 값이 있는데, 더 작은 값을 채택해서 12로 결정합니다.

방문하지 않은 노드들 중에서 거리가 12가 가장 낮으므로 4를 탐색합니다.


1 2 3 4 5
0 8 3 12 23 / 12 + 5

(5,5) 이므로 12 + 5 해서 17이 되므로 더 작은 17이 체택됩니다.

 이런식으로 5까지 탐색해서 모든 노드의 방문을 마치면 최단거리 탐색이 끝납니다.


파이썬 코드

adjacent는 인접리스트를 의미합니다.

거리가 가장 작은 노드를 골라서 탐색해야되는데, 거리가 작은 노드를 고르는게 시간 복잡도가 큽니다.

그러므로 우선순위 큐를 사용합니다.

(거리, 노드번호) 이렇게 튜플로 넣으면 첫째항인 거리를 우선순위로 내림차순으로 정렬합니다.

1부터 시작해서 a,b를 경유후, n에 도착해야합니다.

 

1 -> a -> b -> n

1 -> b -> a -> n

그러므로 이 두가지 경로가 있다는 것을 특히 유의해야 합니다.

#dijkstra
import sys
from queue import PriorityQueue
input = sys.stdin.readline
n,m = map(int, input().split())
adjacent = [[] for _ in range(n+1)]
for _ in range(m):
    a,b,w = map(int, input().split())
    adjacent[a].append((b,w))
    adjacent[b].append((a,w))
a,b = map(int, input().split())
possible = True
def dijkstra(start , end):
    global possible
    dist = [sys.maxsize] * (n+1)
    dist[start] = 0
    visited = [False] * (n+1)
    queue = PriorityQueue()
    queue.put((0 , start)) # (거리 , 노드)
    while queue.qsize() != 0:
        node = queue.get()
        if visited[node[1]]:
            continue
        visited[node[1]] = True
        for i in adjacent[node[1]]:
            distance = node[0] + i[1]
            if distance < dist[i[0]]:
                dist[i[0]] = distance
                queue.put((distance , i[0]))
    if dist[end] == sys.maxsize:
        possible = False
        return 0
    return dist[end]

result_1 =  dijkstra(1,a) + dijkstra(a, b) + dijkstra(b, n)
result_2 =  dijkstra(1,b) + dijkstra(b, a) + dijkstra(a, n)
if possible:
    print(min(result_1,result_2))
else:
    print("-1")
반응형

댓글