14_shortest_path.py

Download
python 337 lines 10.1 KB
  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()