Keep Going

[μ΅œλ‹¨κ±°λ¦¬] ν”Œλ‘œμ΄λ“œ 와샬 μ•Œκ³ λ¦¬μ¦˜ λ³Έλ¬Έ

Problem Solving

[μ΅œλ‹¨κ±°λ¦¬] ν”Œλ‘œμ΄λ“œ 와샬 μ•Œκ³ λ¦¬μ¦˜

seon 2024. 6. 17.
λ°˜μ‘ν˜•

ν”Œλ‘œμ΄λ“œ 와샬 νŠΉμ§•

    • μš©λ„: λͺ¨λ“  쌍의 μ΅œλ‹¨ 경둜λ₯Ό μ°ΎλŠ” μ•Œκ³ λ¦¬μ¦˜
    • κ·Έλž˜ν”„ μœ ν˜•: 음의 κ°€μ€‘μΉ˜λ₯Ό ν—ˆμš©ν•˜μ§€λ§Œ, 음의 사이클이 μ—†λŠ” κ·Έλž˜ν”„
    • μž₯점: κ΅¬ν˜„μ΄ κ°„λ‹¨ν•˜κ³ , λͺ¨λ“  쌍의 μ΅œλ‹¨ 경둜λ₯Ό ꡬ할 수 있음
      • DPλ₯Ό μ‚¬μš©ν•΄μ„œ κ΅¬ν˜„ν•œλ‹€.
    • 단점: μ‹œκ°„ λ³΅μž‘λ„κ°€ λ†’μ•„ 큰 κ·Έλž˜ν”„μ—μ„œλŠ” λΉ„νš¨μœ¨μ 

μ•Œκ³ λ¦¬μ¦˜

μ‹œκ°„ λ³΅μž‘λ„ O(V^3)

  • dpλ₯Ό μ‚¬μš©ν•΄μ„œ κ·Έλž˜ν”„μ˜ λͺ¨λ“  정점 쌍 μ‚¬μ΄μ˜ μ΅œλ‹¨ 경둜λ₯Ό κ΅¬ν•œλ‹€. 3쀑 루프λ₯Ό ν†΅ν•΄μ„œ λͺ¨λ“  κ°€λŠ₯ν•œ 경둜λ₯Ό κ²€μ‚¬ν•˜κ³ , μ΅œλ‹¨ 경둜λ₯Ό μ—…λ°μ΄νŠΈ ν•˜λŠ” λ°©μ‹μœΌλ‘œ μž‘λ™ν•œλ‹€.
  • dpλ₯Ό μ‚¬μš©ν•œλ‹€κ΅¬μš” ?
    • ν”Œλ‘œμ΄λ“œ μ›Œμƒ¬μ€ “κ±°μ³κ°€λŠ” 정점”을 μ΄μš©ν•΄μ„œ μ΅œλ‹¨ 경둜λ₯Ό μ μ§„μ μœΌλ‘œ κ°œμ„ ν•˜λŠ” 방법이닀.
    • iμ—μ„œ j둜 κ°€λŠ” μ΅œλ‹¨ 거리가 정점 kλ₯Ό κ±°μ³κ°€λŠ” 경우 더 μ§§μ•„μ§ˆ 수 μžˆλ‹€λ©΄, κ·Έ κ°’μœΌλ‘œ μ΅œλ‹¨ 거리λ₯Ό κ°±μ‹ ν•˜λŠ” 방식이닀.

1. μ΅œλ‹¨κ±°λ¦¬ λ°°μ—΄ μ΄ˆκΈ°ν™” - ν”Œλ‘œμ΄λ“œ μ›Œμƒ¬μ„ μ‚¬μš©ν•˜κΈ° μœ„ν•œ μ„ΈνŒ…

  • 2차원 배열을 μ‚¬μš©ν•΄μ„œ μ΅œλ‹¨κ±°λ¦¬ λ°°μ—΄ distλ₯Ό λ§Œλ“ λ‹€.
  • dist[i][j] λŠ” iμ—μ„œ j둜 κ°€λŠ” μ΅œλ‹¨ 거리λ₯Ό μ˜λ―Έν•œλ‹€.
  • distλ₯Ό int(1e9)으둜 μ΄ˆκΈ°ν™” ν•  것인데,
  • 자기 μžμ‹ μœΌλ‘œ κ°€λŠ” κ±°λ¦¬λŠ” 0으둜 μ΄ˆκΈ°ν™”
  • 직접 μ—°κ²°λœ 간선은 ν•΄λ‹Ή κ°€μ€‘μΉ˜λ‘œ μ΄ˆκΈ°ν™”
for i in range(n):
    for j in range(n):
        if i == j:
            dist[i][j] = 0
        else:
            dist[i][j] = graph[i][j] if (i, j) in graph else float('inf')

2. 점진적 κ°œμ„  - ν”Œλ‘œμ΄λ“œ μ›Œμƒ¬μ˜ 꽃

  • iλΆ€ν„° jκΉŒμ§€ κ°€λŠ”λ° λͺ¨λ“  κ°€λŠ₯ν•œ kλ₯Ό λ“€λ¦¬λŠ” 경우λ₯Ό κ³ λ €ν•˜λ €κ³  ν•œλ‹€ !!! 정점 kλ₯Ό κ±°μ³κ°€λŠ” κ²½λ‘œκ°€ 기쑴의 κ²½λ‘œλ³΄λ‹€ 더 짧은지 ν™•μΈν•˜κ³ , 짧으면 κ°±μ‹ ν•˜λŠ” 방식이닀. (=경둜 i -> jλ₯Ό 경둜 i -> k -> j와 λΉ„κ΅ν•˜μ—¬ 더 짧은 경둜λ₯Ό 찾으면 κ°±μ‹ )
  • : ν”Œλ‘œμ΄λ“œ μ›Œμƒ¬μ˜ 핡심은 iμ—μ„œ jκΉŒμ§€ κ°€λŠ”λ° kλ₯Ό 듀리면 더 μ΅œλ‹¨ 거리가 λ°œμƒν•  수 μžˆλ‹€λŠ” κ²ƒμ΄λ‹ˆκΉŒ !!
  • 3개의 쀑첩 for문을 μ‚¬μš©ν•΄μ„œ λͺ¨λ“  쌍 (i, j)에 λŒ€ν•΄ λͺ¨λ“  쀑간 정점 kλ₯Ό κ³ λ €ν•œλ‹€.
  • dist[i][j]λŠ” dist[i][k] + dist[k][j]보닀 크면, dist[i][j]λ₯Ό κ°±μ‹ ν•œλ‹€.
for k in range(n):
    for i in range(n):
        for j in range(n):
            if dist[i][j] > dist[i][k] + dist[k][j]:
                dist[i][j] = dist[i][k] + dist[k][j]

3. 음의 사이클 감지

  • ν”Œλ‘œμ΄λ“œ 와샬은 음의 κ°€μ€‘μΉ˜μΌ λ•Œ μ‚¬μš© κ°€λŠ₯ν•˜μ§€λ§Œ, 음의 사이클이 있으면 μ•ˆλœλ‹€. λ”°λΌμ„œ 음의 사이클을 κ°μ§€ν•˜λŠ” 것이 ν•„μš”ν•˜λ‹€ !
  • dist[i][i]λŠ” iμ—μ„œ μ‹œμž‘ν•΄μ„œ i둜 λŒμ•„μ˜€λŠ” μ΅œλ‹¨ 경둜λ₯Ό μ˜λ―Έν•œλ‹€. μ΄ˆκΈ°ν™” ν•  λ•Œ dist[i][i]=0으둜 μ„€μ •λœλ‹€. 그런데 ν”Œλ‘œμ΄λ“œ 와샬을 λ‹€ 돈 후에 이것이 음수라면, 음의 사이클이 μ‘΄μž¬ν•œλ‹€κ³  νŒλ‹¨ν•œλ‹€.
    • μ™œλƒν•˜λ©΄ iμ—μ„œ μΆœλ°œν•΄μ„œ i둜 λŒμ•„μ˜€λŠ” 경둜의 총 κ°€μ€‘μΉ˜κ°€ μŒμˆ˜μ΄λΌλŠ” 뜻이기 λ•Œλ¬Έμ΄λ‹€. 2κ°€μ§€ κ²½μš°κ°€ μ‘΄μž¬ν•  수 μžˆλ‹€.
      • 직접적인 음의 사이클: λ…Έλ“œ iμ—μ„œ μ‹œμž‘ν•˜μ—¬ λ‹€μ‹œ λ…Έλ“œ i둜 λŒμ•„μ˜€λŠ” κ²½λ‘œμ— 음의 사이클이 μ‘΄μž¬ν•˜λŠ” 경우.
      • 간접적인 음의 사이클: μ—¬λŸ¬ 정점을 거쳐 λ‹€μ‹œ λ…Έλ“œ i둜 λŒμ•„μ˜€λŠ” κ²½λ‘œμ— 음의 사이클이 ν¬ν•¨λœ 경우.
# 음의 사이클 감지
for i in range(n):
    if dist[i][i] < 0:
        print("Graph contains a negative weight cycle")
        return None

κ΅¬ν˜„

def floyd_warshall(n, graph):
    # 거리 ν…Œμ΄λΈ” μ΄ˆκΈ°ν™”
    dist = [[int(1e9)] * n for _ in range(n)]
    for i in range(n):
        dist[i][i] = 0

    for u in range(n):
        for v, w in graph[u]:
            dist[u][v] = w

    # ν”Œλ‘œμ΄λ“œ μ›Œμƒ¬ μ•Œκ³ λ¦¬μ¦˜
    for k in range(n):
        for i in range(n):
            for j in range(n):
                if dist[i][j] > dist[i][k] + dist[k][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]

    # 음의 사이클 감지
    for i in range(n):
        if dist[i][i] < 0:
            print("Graph contains a negative weight cycle")
            return None

    return dist

# 예제 κ·Έλž˜ν”„
n = 4
graph = [
    [(1, 1), (2, 4)],
    [(2, -3)],
    [(3, 2)],
    [(0, -1)]
]

distances = floyd_warshall(n, graph)
if distances:
    for row in distances:
        print(row)
λ°˜μ‘ν˜•