그래프2 - 플로이드

nhwang·2022년 6월 29일
0

플로이드 알고리즘

모든 정점 쌍 사이의 최단거리를 구해주는 알고리즘
(방향 / 무방향 상관없음)

1번 노드를 반드시 거치는 것과 현재 테이블의 값과 비교 후 비용의 총합이 더 작은 것을 갱신
2. 1번 을 노드의 수만큼 반복

복잡도 : O(V**3)
정점 450개면 약 1억 연산이지만 플로이드는 단순 사칙연산이라 정점 1000개 근사치 가능

-구현 : 자주쓰던 인접리스트가 아니라, 인접행렬 그자체를 쓰는 테이블임
start==end 부분은 나중에 한번에 0처리 하기 위해 continue로 넘어가도록 한다.

import sys

n = int(input())
e = int(input())
matt = [[10001000]*(n+1) for __ in range(n+1)]
for _ in range(e):
    s,t,cost = map(int, sys.stdin.readline().strip().split())
    if matt[s][t] > cost:
        matt[s][t] = cost
        #matt[t][s] = cost 무방향

for node in range(1, n+1):
    for st in range(1, n+1):
        for end in range(1, n+1):
            if st==end:
                continue #같은 곳일때는 연산하지 않는게 낫다. 나중에 10001000을 한번에 0 만들거임
            if (matt[st][node] + matt[node][end]) < matt[st][end]:
                matt[st][end] = (matt[st][node] + matt[node][end])
                #matt[end][st] = (matt[st][node] + matt[node][end]) 무방향

for ii in range(1, n+1):
    for jj in range(1, n+1):
        if matt[ii][jj] == 10001000:
            matt[ii][jj] = 0         #문제의 조건에 따라 처리한다.
        print(matt[ii][jj], end = ' ')
    print("", end = '\n')

무방향일때 하나만 넣을 수 있고, 문제의 조건에 따라 y,x 초기화 잘해야 한다.
최대값은 간선의 최댓값이 아닌, n*간선의 최대값을 주는 것에 주의한다.


경로를 추적할 때

nxt 배열을 만든다

nxt : table배열이 갱신될 때, 반드시 지나가는 [node]를 담는 배열

참고자료 및 출처 : 바킹독 알고리즘 강의(유튜브) (🙏🙏🙏) > 링크

  1. nxt 초기화 : 직접적으로 이어진 것만 배열에 담는다.

  2. 최단거리 table이 갱신될때 [node]를 반드시 거쳐가므로
    해당 하는 위치인 nxt[start][end] 에다가 nxt[start][node]의 값을 담는다. (노드를 거쳐서 end로 가야한다는 의미)
    node = 1일때
    node = 2일때

  3. 계속 덮어쓰면서 최종 nxt를 만든다

테이블이 다 완성되었으므로 경로를 추적한다.

경로 추적 방법

nxt[tempstart][end]의 값을 봐서 그 값이 end가 될때까지 end의 값을 tempstart로 초기화하고
만나는 값마다가 지나는 노드가 된다.

-구현

for ss in range(1,n+1):
    for endd in range(1, n+1):
        if nxt[ss][endd]==0:# 경로가 없는 경우
            print(0)
            continue
        q = deque()
        tst = ss
        q.appendleft(ss)
        while(1):
            q.appendleft(nxt[tst][endd])
            if nxt[tst][endd]==endd: #or 추가. 아무 연결이 없는 경우 생각필요함
                break
            tst=nxt[tst][endd]

경로 추적 전체 코드 (11780)

import sys
from collections import deque

n = int(input())
m = int(input())
table = [[10000010]*(n+1) for __ in range(n+1)]
nxt = [[0]*(n+1) for __ in range(n+1)] #nxt도 초기화 고민필요
for _ in range(m):
 s, e, cost = map(int, sys.stdin.readline().strip().split())
 if table[s][e] > cost:
     table[s][e] = cost
     #table[e][s] = cost 무방향
     nxt[s][e] = e
     #nxt[e][s] = s 무방향 nxt는 무방향이어도 비대칭성이다

for node in range(1, n+1):
 for st in range(1, n+1):
     for end in range(1,n+1):
         if st==end:
             continue
         if (table[st][node]+table[node][end]) < table[st][end]:
             table[st][end] = (table[st][node]+table[node][end])
             nxt[st][end] = nxt[st][node]
             #mmap[end][st] = (mmap[st][node]+mmap[node][end]) 무방향 : 값은  동일하니 유지
             #nxt[end][st] = nxt[end][node] 무방향:의미적으로 이게 맞다

for tt in range(1,n+1):
 for ff in range(1,n+1):
     if table[tt][ff] == 10000010:
         table[tt][ff] = 0
         nxt[tt][ff] = 0


for t1 in range(1, n+1):
 for t2 in range(1, n+1):
     print(table[t1][t2], end = ' ')
 print("")

for ss in range(1,n+1):
 for endd in range(1, n+1):
     if nxt[ss][endd]==0:#ss == endd랑 헷갈렸음.
         print(0)
         continue
     q = deque()
     tst = ss
     q.appendleft(ss)
     while(1):
         q.appendleft(nxt[tst][endd])
         if nxt[tst][endd]==endd:
             break
         tst=nxt[tst][endd]
     print(len(q),end = ' ')
     while(q):
         print(q.pop(),end = ' ')
     print("",end = '\n')

주석 부분 주의:
(주석)ss == endd랑 헷갈렸음. 부분을 ss==end로 해버리면 시작점 끝점이 같은경우만 따지지만
사실 경로가 존재하지 않는 경우를 따지지 못해 아래의 while문으로 들어가게 된다.

아래 while은 nxt[tst][endd]==endd를 만나기전까지 루프를 돌지만 경로가 없으면 무한루프이다.

profile
42Seoul

0개의 댓글