1"""
2๋ค์ต์คํธ๋ผ ์๊ณ ๋ฆฌ์ฆ (Dijkstra's Algorithm)
3Dijkstra's Shortest Path Algorithm
4
5๊ฐ์ค์น๊ฐ ์๋ ๊ทธ๋ํ์์ ๋จ์ผ ์ถ๋ฐ์ ์ต๋จ ๊ฒฝ๋ก๋ฅผ ์ฐพ๋ ์๊ณ ๋ฆฌ์ฆ์
๋๋ค.
6์์ ๊ฐ์ค์น๊ฐ ์๋ ๊ทธ๋ํ์์ ์ฌ์ฉํฉ๋๋ค.
7"""
8
9import heapq
10from collections import defaultdict
11from typing import List, Dict, Tuple, Optional
12
13
14# =============================================================================
15# ๊ฐ์ค์น ๊ทธ๋ํ ํํ
16# =============================================================================
17def create_weighted_graph(edges: List[Tuple[int, int, int]], directed: bool = False) -> Dict[int, List[Tuple[int, int]]]:
18 """
19 ๊ฐ์ ๋ฆฌ์คํธ (u, v, weight)๋ก๋ถํฐ ๊ฐ์ค์น ์ธ์ ๋ฆฌ์คํธ ์์ฑ
20 graph[u] = [(v, weight), ...]
21 """
22 graph = defaultdict(list)
23 for u, v, w in edges:
24 graph[u].append((v, w))
25 if not directed:
26 graph[v].append((u, w))
27 return graph
28
29
30# =============================================================================
31# 1. ๋ค์ต์คํธ๋ผ ๊ธฐ๋ณธ ๊ตฌํ
32# =============================================================================
33def dijkstra(graph: Dict[int, List[Tuple[int, int]]], start: int, n: int) -> List[int]:
34 """
35 ๋ค์ต์คํธ๋ผ ์๊ณ ๋ฆฌ์ฆ (์ฐ์ ์์ ํ ์ฌ์ฉ)
36 ์๊ฐ๋ณต์ก๋: O((V + E) log V)
37
38 Args:
39 graph: ์ธ์ ๋ฆฌ์คํธ (๋
ธ๋ -> [(์ด์, ๊ฐ์ค์น), ...])
40 start: ์์ ๋
ธ๋
41 n: ์ด ๋
ธ๋ ์
42
43 Returns:
44 ๊ฐ ๋
ธ๋๊น์ง์ ์ต๋จ ๊ฑฐ๋ฆฌ ๋ฐฐ์ด (๋๋ฌ ๋ถ๊ฐ์ ๋ฌดํ๋)
45 """
46 INF = float('inf')
47 dist = [INF] * n
48 dist[start] = 0
49
50 # (๊ฑฐ๋ฆฌ, ๋
ธ๋) ํํ์ ์ ์ฅํ๋ ์ต์ ํ
51 pq = [(0, start)]
52
53 while pq:
54 d, u = heapq.heappop(pq)
55
56 # ์ด๋ฏธ ์ฒ๋ฆฌ๋ ๊ฑฐ๋ฆฌ๋ณด๋ค ํฌ๋ฉด ์คํต
57 if d > dist[u]:
58 continue
59
60 # ์ธ์ ๋
ธ๋ ํ์ธ
61 for v, weight in graph[u]:
62 new_dist = dist[u] + weight
63 if new_dist < dist[v]:
64 dist[v] = new_dist
65 heapq.heappush(pq, (new_dist, v))
66
67 return dist
68
69
70# =============================================================================
71# 2. ๋ค์ต์คํธ๋ผ + ๊ฒฝ๋ก ์ถ์
72# =============================================================================
73def dijkstra_with_path(
74 graph: Dict[int, List[Tuple[int, int]]],
75 start: int,
76 end: int,
77 n: int
78) -> Tuple[int, List[int]]:
79 """
80 ์ต๋จ ๊ฑฐ๋ฆฌ์ ํจ๊ป ์ค์ ๊ฒฝ๋ก๋ ๋ฐํ
81 """
82 INF = float('inf')
83 dist = [INF] * n
84 dist[start] = 0
85 parent = [-1] * n
86
87 pq = [(0, start)]
88
89 while pq:
90 d, u = heapq.heappop(pq)
91
92 if d > dist[u]:
93 continue
94
95 if u == end:
96 break
97
98 for v, weight in graph[u]:
99 new_dist = dist[u] + weight
100 if new_dist < dist[v]:
101 dist[v] = new_dist
102 parent[v] = u
103 heapq.heappush(pq, (new_dist, v))
104
105 # ๊ฒฝ๋ก ๋ณต์
106 if dist[end] == INF:
107 return INF, []
108
109 path = []
110 node = end
111 while node != -1:
112 path.append(node)
113 node = parent[node]
114 path.reverse()
115
116 return dist[end], path
117
118
119# =============================================================================
120# 3. ๋ชจ๋ ์ ์ต๋จ ๊ฒฝ๋ก (ํ๋ก์ด๋-์์
)
121# =============================================================================
122def floyd_warshall(n: int, edges: List[Tuple[int, int, int]]) -> List[List[int]]:
123 """
124 ํ๋ก์ด๋-์์
์๊ณ ๋ฆฌ์ฆ
125 ๋ชจ๋ ์ ์ ์ ์ฌ์ด์ ์ต๋จ ๊ฑฐ๋ฆฌ ๊ณ์ฐ
126 ์๊ฐ๋ณต์ก๋: O(Vยณ)
127 """
128 INF = float('inf')
129 dist = [[INF] * n for _ in range(n)]
130
131 # ์๊ธฐ ์์ ์ผ๋ก์ ๊ฑฐ๋ฆฌ๋ 0
132 for i in range(n):
133 dist[i][i] = 0
134
135 # ๊ฐ์ ์ ๋ณด ๋ฐ์
136 for u, v, w in edges:
137 dist[u][v] = w
138
139 # k๋ฅผ ๊ฒฝ์ ํ๋ ๊ฒฝ๋ก ๊ณ ๋ ค
140 for k in range(n):
141 for i in range(n):
142 for j in range(n):
143 if dist[i][k] + dist[k][j] < dist[i][j]:
144 dist[i][j] = dist[i][k] + dist[k][j]
145
146 return dist
147
148
149# =============================================================================
150# 4. ๋ฒจ๋ง-ํฌ๋ ์๊ณ ๋ฆฌ์ฆ (์์ ๊ฐ์ค์น ํ์ฉ)
151# =============================================================================
152def bellman_ford(n: int, edges: List[Tuple[int, int, int]], start: int) -> Tuple[List[int], bool]:
153 """
154 ๋ฒจ๋ง-ํฌ๋ ์๊ณ ๋ฆฌ์ฆ
155 ์์ ๊ฐ์ค์น๋ฅผ ํ์ฉํ๋ฉฐ, ์์ ์ฌ์ดํด ๊ฒ์ถ ๊ฐ๋ฅ
156 ์๊ฐ๋ณต์ก๋: O(VE)
157
158 Returns:
159 (์ต๋จ ๊ฑฐ๋ฆฌ ๋ฐฐ์ด, ์์ ์ฌ์ดํด ์กด์ฌ ์ฌ๋ถ)
160 """
161 INF = float('inf')
162 dist = [INF] * n
163 dist[start] = 0
164
165 # V-1๋ฒ ๋ฐ๋ณต
166 for _ in range(n - 1):
167 for u, v, w in edges:
168 if dist[u] != INF and dist[u] + w < dist[v]:
169 dist[v] = dist[u] + w
170
171 # ์์ ์ฌ์ดํด ๊ฒ์ถ (ํ ๋ฒ ๋ ๋ฐ๋ณตํด์ ๊ฐฑ์ ๋๋ฉด ์์ ์ฌ์ดํด)
172 has_negative_cycle = False
173 for u, v, w in edges:
174 if dist[u] != INF and dist[u] + w < dist[v]:
175 has_negative_cycle = True
176 break
177
178 return dist, has_negative_cycle
179
180
181# =============================================================================
182# 5. ๋คํธ์ํฌ ์ง์ฐ ์๊ฐ ๋ฌธ์
183# =============================================================================
184def network_delay_time(times: List[List[int]], n: int, k: int) -> int:
185 """
186 ๋
ธ๋ k์์ ๋ชจ๋ ๋
ธ๋๋ก ์ ํธ๊ฐ ๋๋ฌํ๋ ์ต์ ์๊ฐ
187 times[i] = [source, target, time]
188 ๋๋ฌ ๋ถ๊ฐ๋ฅํ๋ฉด -1 ๋ฐํ
189 """
190 graph = defaultdict(list)
191 for u, v, w in times:
192 graph[u].append((v, w))
193
194 dist = dijkstra(graph, k, n + 1) # ๋
ธ๋ ๋ฒํธ๊ฐ 1๋ถํฐ ์์
195
196 # 1~n๋ฒ ๋
ธ๋ ์ค ์ต๋ ๊ฑฐ๋ฆฌ
197 max_time = max(dist[1:n + 1])
198
199 return max_time if max_time != float('inf') else -1
200
201
202# =============================================================================
203# 6. K๋ฒ์งธ ์ต๋จ ๊ฒฝ๋ก
204# =============================================================================
205def kth_shortest_path(
206 graph: Dict[int, List[Tuple[int, int]]],
207 start: int,
208 end: int,
209 k: int,
210 n: int
211) -> int:
212 """
213 K๋ฒ์งธ๋ก ์งง์ ๊ฒฝ๋ก์ ๊ธธ์ด ๋ฐํ
214 ์ฐพ์ ์ ์์ผ๋ฉด -1 ๋ฐํ
215 """
216 INF = float('inf')
217 count = [0] * n # ๊ฐ ๋
ธ๋์ ๋์ฐฉํ ํ์
218 pq = [(0, start)]
219
220 while pq:
221 d, u = heapq.heappop(pq)
222 count[u] += 1
223
224 if u == end and count[u] == k:
225 return d
226
227 # k๋ฒ ์ด์ ๋ฐฉ๋ฌธํ ๋
ธ๋๋ ๋ ์ด์ ํ์ฅํ์ง ์์
228 if count[u] > k:
229 continue
230
231 for v, weight in graph[u]:
232 heapq.heappush(pq, (d + weight, v))
233
234 return -1
235
236
237# =============================================================================
238# ํ
์คํธ
239# =============================================================================
240def main():
241 print("=" * 60)
242 print("๋ค์ต์คํธ๋ผ & ์ต๋จ ๊ฒฝ๋ก ์๊ณ ๋ฆฌ์ฆ")
243 print("=" * 60)
244
245 # ์์ ๊ทธ๋ํ
246 # 1
247 # 0 ---> 1
248 # | |
249 # 4 | | 2
250 # v v
251 # 2 ---> 3
252 # 3
253 edges = [
254 (0, 1, 1),
255 (0, 2, 4),
256 (1, 3, 2),
257 (2, 3, 3),
258 (1, 2, 2)
259 ]
260
261 print("\n[๊ทธ๋ํ ๊ตฌ์กฐ]")
262 print(" 0 --1--> 1")
263 print(" | |")
264 print(" 4 2")
265 print(" v v")
266 print(" 2 --3--> 3")
267 print(" (1->2 ๊ฐ์ค์น 2)")
268
269 # 1. ๋ค์ต์คํธ๋ผ ๊ธฐ๋ณธ
270 print("\n[1] ๋ค์ต์คํธ๋ผ ๊ธฐ๋ณธ")
271 graph = create_weighted_graph(edges, directed=True)
272 dist = dijkstra(graph, 0, 4)
273 print(f" ์์์ : 0")
274 for i, d in enumerate(dist):
275 print(f" ๋
ธ๋ {i}๊น์ง ๊ฑฐ๋ฆฌ: {d}")
276
277 # 2. ๋ค์ต์คํธ๋ผ + ๊ฒฝ๋ก
278 print("\n[2] ๋ค์ต์คํธ๋ผ + ๊ฒฝ๋ก ์ถ์ ")
279 distance, path = dijkstra_with_path(graph, 0, 3, 4)
280 print(f" 0 -> 3 ์ต๋จ ๊ฑฐ๋ฆฌ: {distance}")
281 print(f" ๊ฒฝ๋ก: {' -> '.join(map(str, path))}")
282
283 # 3. ํ๋ก์ด๋-์์
284 print("\n[3] ํ๋ก์ด๋-์์
(๋ชจ๋ ์ ์ต๋จ ๊ฑฐ๋ฆฌ)")
285 all_dist = floyd_warshall(4, edges)
286 print(" ๊ฑฐ๋ฆฌ ํ๋ ฌ:")
287 for i, row in enumerate(all_dist):
288 row_str = [str(d) if d != float('inf') else 'โ' for d in row]
289 print(f" {i}: {row_str}")
290
291 # 4. ๋ฒจ๋ง-ํฌ๋
292 print("\n[4] ๋ฒจ๋ง-ํฌ๋ (์์ ๊ฐ์ค์น ํ์ฉ)")
293 edges_negative = [(0, 1, 4), (0, 2, 5), (1, 2, -3), (2, 3, 4)]
294 dist, has_neg_cycle = bellman_ford(4, edges_negative, 0)
295 print(f" ๊ฐ์ : {edges_negative}")
296 print(f" ์ต๋จ ๊ฑฐ๋ฆฌ: {dist}")
297 print(f" ์์ ์ฌ์ดํด: {has_neg_cycle}")
298
299 # ์์ ์ฌ์ดํด ์์
300 edges_neg_cycle = [(0, 1, 1), (1, 2, -1), (2, 0, -1)]
301 dist, has_neg_cycle = bellman_ford(3, edges_neg_cycle, 0)
302 print(f"\n ์์ ์ฌ์ดํด ๊ทธ๋ํ: {edges_neg_cycle}")
303 print(f" ์์ ์ฌ์ดํด ์กด์ฌ: {has_neg_cycle}")
304
305 # 5. ๋คํธ์ํฌ ์ง์ฐ ์๊ฐ
306 print("\n[5] ๋คํธ์ํฌ ์ง์ฐ ์๊ฐ")
307 times = [[2, 1, 1], [2, 3, 1], [3, 4, 1]]
308 n, k = 4, 2
309 result = network_delay_time(times, n, k)
310 print(f" times={times}, n={n}, k={k}")
311 print(f" ๋ชจ๋ ๋
ธ๋ ๋๋ฌ ์๊ฐ: {result}")
312
313 # 6. K๋ฒ์งธ ์ต๋จ ๊ฒฝ๋ก
314 print("\n[6] K๋ฒ์งธ ์ต๋จ ๊ฒฝ๋ก")
315 edges_k = [(0, 1, 1), (0, 2, 3), (1, 2, 1), (1, 3, 2), (2, 3, 1)]
316 graph_k = create_weighted_graph(edges_k, directed=True)
317 for k in range(1, 4):
318 dist = kth_shortest_path(graph_k, 0, 3, k, 4)
319 print(f" 0->3 {k}๋ฒ์งธ ์ต๋จ ๊ฒฝ๋ก: {dist}")
320
321 print("\n" + "=" * 60)
322 print("์ฃผ์ ์๊ณ ๋ฆฌ์ฆ ๋น๊ต")
323 print("=" * 60)
324 print("""
325 | ์๊ณ ๋ฆฌ์ฆ | ์๊ฐ๋ณต์ก๋ | ์์ ๊ฐ์ค์น | ์ฉ๋ |
326 |---------------|----------------|------------|---------------------|
327 | ๋ค์ต์คํธ๋ผ | O((V+E)log V) | ๋ถ๊ฐ | ๋จ์ผ ์ถ๋ฐ์ ์ต๋จ๊ฑฐ๋ฆฌ |
328 | ๋ฒจ๋ง-ํฌ๋ | O(VE) | ๊ฐ๋ฅ | ์์ ๊ฐ์ค์น/์ฌ์ดํด |
329 | ํ๋ก์ด๋-์์
| O(Vยณ) | ๊ฐ๋ฅ* | ๋ชจ๋ ์ ์ต๋จ๊ฑฐ๋ฆฌ |
330
331 * ํ๋ก์ด๋-์์
๋ ์์ ์ฌ์ดํด์ด ์์ผ๋ฉด ์ฌ๋ฐ๋ฅด์ง ์์
332 """)
333
334
335if __name__ == "__main__":
336 main()