|
2 | 2 |
|
3 | 3 | **최단 경로 알고리즘**은 주어진 그래프에서 주어진 두 정점을 연결하는 가장 짧은 경로의 길이를 찾는 알고리즘입니다.
|
4 | 4 |
|
5 |
| -가중치가 없는 그래프에서는 너비 우선 탐색으로 구할 수 있었습니다. |
| 5 | +이전에 살펴봤다시피 가중치가 없는 그래프에서는 너비 우선 탐색으로 구할 수 있었습니다. |
6 | 6 |
|
7 | 7 | 여기서는 가중치가 있는 그래프만을 다루며, 음수 가중치를 갖는 간선이 있을 수 있습니다.
|
8 | 8 |
|
|
48 | 48 |
|
49 | 49 | 기본적인 아이디어는 너비 우선 탐색과 동일하며, 너비 우선 탐색과 달리 *우선순위 큐*를 사용합니다.
|
50 | 50 |
|
51 |
| -정점의 개수가 $V$, 간선의 개수가 $E$일 때, 다익스트라 알고리즘의 시간복잡도는 $O(ElgV)$입니다. |
52 |
| - |
53 | 51 | <br>
|
54 | 52 |
|
55 | 53 | ### 작동방식
|
|
64 | 62 |
|
65 | 63 | 구체적으로는 아래의 과정을 거칩니다.
|
66 | 64 |
|
67 |
| -1. 시작점으로부터 각 정점까지의 최단 거리를 저장하는 배열 `dist[]`를 `inf`로 초기화합니다. |
68 |
| -1. `dist[src]`의 값을 `0`으로 대입합니다. (시작점) |
69 |
| -1. 우선순위 큐에 `(src, 0)`을 삽입합니다. |
70 |
| -1. 우선순위 큐가 다 비어질 때까지 다음을 반복합니다. |
71 |
| - 1. 우선순위 큐에서 현재 정점과 현재 최단 거리 쌍을`(here, cost)` 꺼냅니다. |
72 |
| - 1. 만약 `dist[here]`가 현재 `cost`보다 작다면 지금 꺼낸 것을 무시합니다. |
73 |
| - 1. 현재 정점에 인접한 정점들을 모두 검사합니다. |
74 |
| - 1. 이웃 정점(`there`)까지의 거리를 갱신합니다. |
75 |
| - 1. 그 거리가 `dist[there]`에 저장된 값보다 짧다면 우선순위 큐에 해당 정점을 삽입합니다. |
76 |
| -1. 위 과정을 마치면 `dist[]`에는 최단 거리가 모두 저장됩니다. |
| 65 | + |
| 66 | + |
| 67 | +| s | a | b | c | d | e | |
| 68 | +| ----- | --- | --- | --- | --- | --- | |
| 69 | +| **0** | INF | INF | INF | INF | INF | |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | +| s | a | b | c | d | e | |
| 74 | +| --- | ----- | ----- | --- | --- | ------ | |
| 75 | +| 0 | **7** | **9** | INF | INF | **14** | |
| 76 | + |
| 77 | + |
| 78 | + |
| 79 | +| s | a | b | c | d | e | |
| 80 | +| --- | --- | ----- | ------ | --- | --- | |
| 81 | +| 0 | 7 | **9** | **22** | INF | 14 | |
| 82 | + |
| 83 | + |
| 84 | + |
| 85 | +| s | a | b | c | d | e | |
| 86 | +| --- | --- | --- | ------ | --- | ------ | |
| 87 | +| 0 | 7 | 9 | **20** | INF | **11** | |
| 88 | + |
| 89 | + |
| 90 | + |
| 91 | +| s | a | b | c | d | e | |
| 92 | +| --- | --- | --- | --- | ------ | --- | |
| 93 | +| 0 | 7 | 9 | 20 | **20** | 11 | |
| 94 | + |
| 95 | + |
| 96 | + |
| 97 | +| s | a | b | c | d | e | |
| 98 | +| --- | --- | --- | --- | --- | --- | |
| 99 | +| 0 | 7 | 9 | 20 | 20 | 11 | |
| 100 | + |
| 101 | + |
| 102 | + |
| 103 | +| s | a | b | c | d | e | |
| 104 | +| --- | --- | --- | --- | --- | --- | |
| 105 | +| 0 | 7 | 9 | 20 | 20 | 11 | |
| 106 | + |
| 107 | +이러한 과정을 코드로 작성하면 아래와 같습니다. |
| 108 | + |
| 109 | +```cpp |
| 110 | +#define MAX_V 100 |
| 111 | +#define INF 987654321 |
| 112 | + |
| 113 | +#include <vector> |
| 114 | +#include <queue> |
| 115 | + |
| 116 | +using namespace std; |
| 117 | + |
| 118 | +int V; |
| 119 | +vector<pair<int, int> > adj[MAX_V]; |
| 120 | + |
| 121 | +vector<int> dijkstra(int src) { |
| 122 | + vector<int> dist(V, INF); |
| 123 | + dist[src] = 0; |
| 124 | + |
| 125 | + priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int,int> > > pq; |
| 126 | + pq.push(make_pair(0, src)); |
| 127 | + |
| 128 | + while (!pq.empty()) { |
| 129 | + int cost = pq.top().first; |
| 130 | + int here = pq.top().second; |
| 131 | + pq.pop(); |
77 | 132 |
|
78 |
| -[이러한 과정을 잘 보여주는 그림을 봅시다.](https://m.blog.naver.com/kks227/220796029558?view=img_1) |
| 133 | + if (dist[here] < cost) continue; |
79 | 134 |
|
80 |
| -[코드는 이렇게 작성됩니다.](dijkstra.cc) |
| 135 | + for (const auto& [there, length] : adj[here]) { |
| 136 | + int nextDist = cost + length; |
| 137 | + |
| 138 | + if (nextDist < dist[there]) { |
| 139 | + dist[there] = nextDist; |
| 140 | + pq.push(make_pair(nextDist, there)); |
| 141 | + } |
| 142 | + } |
| 143 | + } |
| 144 | + return dist; |
| 145 | +} |
| 146 | +``` |
| 147 | +
|
| 148 | +### 시간복잡도 |
| 149 | +
|
| 150 | +정점의 개수가 $V$, 간선의 개수가 $E$일 때, 다익스트라 알고리즘의 시간복잡도는 $O(ElgV)$입니다. |
| 151 | +
|
| 152 | +모든 간선을 한 번씩만 검사하며, 모든 정점이 한 번 이상 우선순위 큐에 들어가기 때문입니다. |
| 153 | +
|
| 154 | +<br> |
81 | 155 |
|
82 | 156 | ## 벨만-포드
|
83 | 157 |
|
@@ -123,9 +197,47 @@ $(a, b) \rightarrow (b, c) \rightarrow (c, a)$ 의 사이클을 돌 때마다
|
123 | 197 |
|
124 | 198 | 결론적으로, 벨만 포드 알고리즘의 시간 복잡도는 $O(VE)$입니다.
|
125 | 199 |
|
| 200 | +코드는 아래와 같이 작성됩니다. |
| 201 | +
|
126 | 202 | <br>
|
127 | 203 |
|
128 |
| -[코드는 이렇게 작성됩니다.](bellmanFord.cc) |
| 204 | +```cpp |
| 205 | +#define MAX_V 100 |
| 206 | +#define INF 987654321 |
| 207 | +
|
| 208 | +#include <utility> |
| 209 | +#include <vector> |
| 210 | +
|
| 211 | +using namespace std; |
| 212 | +
|
| 213 | +int V; |
| 214 | +vector<pair<int, int> > adj[MAX_V]; |
| 215 | +
|
| 216 | +vector<int> bellmanFord(int src) { |
| 217 | + vector<int> upper(V, INF); |
| 218 | + upper[src] = 0; |
| 219 | +
|
| 220 | + bool updated; |
| 221 | + for (int iter = 0; iter < V; ++iter) { |
| 222 | + updated = false; |
| 223 | +
|
| 224 | + for (int here = 0; here < V; ++here) { |
| 225 | + for (const auto& [there, cost] : adj[here]) { |
| 226 | + if (upper[here] != INF && upper[there] > upper[here] + cost) { |
| 227 | + upper[there] = upper[here] + cost; |
| 228 | + updated = true; |
| 229 | + } |
| 230 | + } |
| 231 | + } |
| 232 | +
|
| 233 | + if (!updated) break; |
| 234 | + } |
| 235 | +
|
| 236 | + if (updated) upper.clear(); |
| 237 | +
|
| 238 | + return upper; |
| 239 | +} |
| 240 | +``` |
129 | 241 |
|
130 | 242 | <br>
|
131 | 243 |
|
@@ -176,7 +288,28 @@ $(a, b) \rightarrow (b, c) \rightarrow (c, a)$ 의 사이클을 돌 때마다
|
176 | 288 |
|
177 | 289 | <br>
|
178 | 290 |
|
179 |
| -[코드는 이렇게 작성됩니다.](floyd.cc) |
| 291 | +코드는 아래와 같이 작성됩니다. |
| 292 | + |
| 293 | +```cpp |
| 294 | +#define MAX_V 100 |
| 295 | +#define INF 987654321 |
| 296 | + |
| 297 | +#include <vector> |
| 298 | + |
| 299 | +using namespace std; |
| 300 | + |
| 301 | +int V; |
| 302 | +int adj[MAX_V][MAX_V]; |
| 303 | + |
| 304 | +void floyd() { |
| 305 | + for (int i = 0; i < V; ++i) adj[i][i] = 0; |
| 306 | + |
| 307 | + for (int k = 0; k < V; ++k) |
| 308 | + for (int i = 0; i < V; ++i) |
| 309 | + for (int j = 0; j < V; ++j) |
| 310 | + adj[i][j] = min(adj[i][j], adj[i][k] + adj[k][j]); |
| 311 | +} |
| 312 | +``` |
180 | 313 |
|
181 | 314 | <br>
|
182 | 315 |
|
|
0 commit comments