우선순위 큐 다익스트라 알고리즘의 코드 구현은 다음 책의 내용을 참조하였다.
http://book.naver.com/bookdb/book_detail.nhn?bid=7058764
923쪽, 다익스트라의 최단 거리 알고리즘의 구현
그리고 다익스트라 알고리즘이 진행되는 과정은 다음 게시물에 수록되어 있다.
http://www.crocus.co.kr/532 :: 다익스트라 개념
아래 내용을 통해 우선순위 큐 다익스트라 알고리즘의
개념 및 소스 코드를 확인하고, 이 코드를 이용해서 풀 수 있는 문제는 다음과 같다.
문제 원본 :: https://www.acmicpc.net/problem/1753
문제 풀이 :: http://programbasic.tistory.com/545
이 코드에서의 핵심은 다음과 같다.
1. priority_queue를 이용함으로써
즉, MinHeap(우선순위 큐)을 이용하여 시간 복잡도를 현저히 줄일 수 있게 되었다.
이때 log|V|가 되는 이유는 일반 다익스트라에서는 가장 최단 거리를 가진 정점을 찾기위해 선형 탐색을 이용하지만,
MinHeap을 이용하면 log|V|만에 최솟값이 가장 위로 올라오도록 정렬되기에 log|V|이다.
2. priority_queue에서는 기본적으로 가장 큰 원소가 pop의 우선순위를 가지기 때문에
가중치가 3, 2, 1이라는 값이 들어왔다면 우선순위는 3, 2, 1이아닌 1, 2, 3이어야 하기에
음의 가중치로 바꾸어 우선순위 큐에 넣는다.(-1, -2, -3으로)
3. A정점에서 B정점으로 가는 값이 이미 있는데 더 작은값이 생긴다면
우선순위 큐에서 찾지 않고, 그냥 push한 후에, 나중에 가중치를 비교하여
가중치가 더 큰 것이었다면 무시하도록 한다.
아래 그림을 통해 우선순위 큐의 알고리즘이 어떻게 작동하는지 확인하자.
(a~d까지를 확인)
1) a번 정점에서 시작한다고 가정한다.
2) a번 정점은 시작점이므로 0의 최단 거리 값을 가지게 된다.
현재 dist 값 (a번 순서로 0,1,2,3 으로 간다고 가정)
dist[0] = 0
3) a번 정점에서 c번 정점으로의 최단 거리는 1임을 알 수 있다.
dist[0] = 0
dist[2] = 1
4) a번 정점에서 b번 정점으로의 최단 거리는 5임을 알 수 있다.
dist[0] = 0
dist[1] = 5
dist[2] = 1
5) a번 정점에서 가장 최단거리는 c이므로 c부터 방문한다.
dist[0] = 0
dist[1] = 5
dist[2] = 1
6) c번 정점에서 d까지의 최단거리는 3이다.
dist[0] = 0
dist[1] = 5
dist[2] = 1
dist[3] = 3
7) c번 정점에서의 방문이 끝났으니 그다음 가장 최단 거리를 가진 d번 정점을 방문한다.
dist[0] = 0
dist[1] = 5
dist[2] = 1
dist[3] = 3
8) d번 정점에서 b정점까지의 최단거리는 5에서 4로 변한다.
dist[0] = 0
dist[1] = 4
dist[2] = 1
dist[3] = 3
여기서 아래의 코드에 의해 5가 아닌 4로 갱신됨을 알 수 있다.
8-1) 만약 d->b의 가중치를 4라고 하자.
a번 정점에서 b번 정점으로 갈 때 최단거리는 7이라고 알고있었지만,
a->b의 5라는 가중치가 존재하고 있다.
따라서 그림 아래의 알고리즘을 통해 비교가 된다.
결국 7 > 5이므로 업데이트 된다. (그림의 내용 참조)
현재 dist 값 (a번 순서로 0,1,2,3 으로 간다고 가정)
dist[0] = 0
dist[1] = 5 (7->5로 바뀜)
dist[2] = 1
dist[3] = 3
9) d번 정점에서 e정점까지의 최단거리는 8이다.
dist[0] = 0
dist[1] = 4
dist[2] = 1
dist[3] = 3
dist[4] = 8
10) b번 정점에서 인접한 정점은 없으므로 그대로 있는다.
dist[0] = 0
dist[1] = 4
dist[2] = 1
dist[3] = 3
dist[4] = 8
11) e번 정점에서 인접한 정점은 없으니 그대로 있는다.
dist[0] = 0
dist[1] = 4
dist[2] = 1
dist[3] = 3
dist[4] = 8
< 소스 코드 >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #include <iostream> #include <vector> #include <queue> #include <limits.h> #include <cstdio> #define MAX_V 20001 using namespace std; // 그래프의 인접 리스트. (연결된 정점 번호, 간선 가중치) 쌍을 담아야 한다. vector< pair<int, int>> adj[MAX_V]; // 정점의 최대 개수 :: MAX_V vector<int> dijkstra(int src, int V, int E) { // V만큼 배열을 INT_MAX로 초기화 vector<int> dist(V, INT_MAX); dist[src] = 0; // 시작점은 0으로 초기화 한다. priority_queue<pair<int, int> > pq; pq.push(make_pair(0, src)); // 시작점을 처음으로 우선순위 큐에 삽입 while (!pq.empty()) { // 우선순위 큐에 음의 가중치로 들어가 있으니 양으로 바꾸어준다. int cost = -pq.top().first; int here = pq.top().second; pq.pop(); // 만약 지금 꺼낸 것보다 더 짧은 경로를 알고 있다면 지금 꺼낸것을 무시한다. if (dist[here] < cost) continue; // 인접한 정점들을 모두 검사. for (int i = 0; i < adj[here].size(); i++) { int there = adj[here][i].first; int nextDist = cost + adj[here][i].second; // 더 짧은 경로를 발견하면, dist[]를 갱신하고 우선순위 큐에 넣는다. // dist 벡터에는 시작점 -> there 위치까지의 최단 거리가 담겨있다. if (dist[there] > nextDist) { dist[there] = nextDist; pq.push(make_pair(-nextDist, there)); /* 여기서 -로 넣는 이유? priority_queue STL은 기본적으로 가장 큰 원소가 위로 가도록 큐를 구성 따라서 거리의 부호를 바꿔서 거리가 작은 정점부터 꺼내지도록 하기 위함 */ } } } return dist; } | Crocus |
'Applied > 알고리즘' 카테고리의 다른 글
전위, 후위 순회를 알 때 트리 구하는 알고리즘 (0) | 2016.12.07 |
---|---|
KMP 알고리즘(KMP Algorithm) (7) | 2016.12.01 |
플로이드 워셜 알고리즘(Floyd Warshall Algorithm) 소스 코드 (2) | 2016.11.17 |
플로이드 워셜 알고리즘(Floyd Warshall Algorithm) 개념 (12) | 2016.11.17 |
벨만 포드 알고리즘(Bellman-Ford Algorithm) 소스 코드 (0) | 2016.11.17 |