Dijkstra Algorithm - 한 노드에서 다른 노드까지 최단 거리
음의 간선(edge)가 없을 때 정상적으로 동작
우선순위 큐를 사용한 경우
import sys
import heapq
INF = int(1e9)
n, m = map(int, input().split())
start = int(sys.stdin.readline())
graph = [[] for _ in range(n+1)]
distance = [INF] * (n+1)
for _ in range(m):
a, b, c = map(int, sys.stdin.readline().split())
# node a to node b, cost = c
graph[a].append((b, c))
# adjacent list
def dijkstra(start):
q = []
heapq.heappush(q, (0, start))
distance[start] = 0
while q:
dist, now = heapq.heappop(q)
if distance[now] < dist:
continue
for i in graph[now]:
cost = dist + i[1]
if cost < distance[i[0]]:
distance[i[0]] = cost
heapq.heappush(q, (cost, i[0]))
dijkstra(start)
for i in range(1, n+1):
if distance[i] == INF:
print("INFINITY")
else:
print(distance[i])
Floyd-Warshall Algorithm - 모든 지점에서 다른 모든 지점까지의 거리
import sys
INF = float('inf')
n, m = map(int, sys.stdin.readline().split())
graph = [[INF] * (n+1) for _ in range(n+1)]
for a in range(1, n+1):
graph[a][a] = 0
for _ in range(m):
a, b, c = map(int, sys.stdin.readline().split())
graph[a][b] = c
# adjacent matrix
for k in range(1, n+1):
for a in range(1, n+1):
for b in range(1, n+1):
graph[a][b] = min(graph[a][b], graph[a][k] + graph[k][b])
for i in range(1, n+1):
print(*graph[i][1:])
문제
import sys
def solution(n, m, x, k, graph):
for l_ in range(n+1):
for a in range(n+1):
for b in range(n+1):
graph[a][b] = min(graph[a][b], graph[a][l_] + graph[l_][b])
return graph[1][k] + graph[k][x] if graph[1][k] + graph[k][x] < int(1e9) else -1
if __name__ == "__main__":
INF = int(1e9)
n, m = map(int, sys.stdin.readline().split())
graph = [[INF] * (n+1) for _ in range(n+1)]
for a in range(n+1):
graph[a][a] = 0
for _ in range(m):
a, b = map(int, sys.stdin.readline().split())
graph[a][b] = 1
graph[b][a] = 1
x, k = map(int, sys.stdin.readline().split())
print(solution(n, m, x, k, graph))
import sys
import heapq
def solution(n, m, c, adj_list):
INF = int(1e9)
distance = [INF] * (n+1)
distance = dijkstra(adj_list, distance, c)
dist_eff = [x for x in distance if x != INF and x != 0]
return len(dist_eff), max(dist_eff)
def dijkstra(adj_list, distance, start):
q = []
heapq.heappush(q, (0, start))
while q:
cost, now = heapq.heappop(q)
if cost > distance[now]:
continue
distance[now] = cost
for i in adj_list[now]:
if cost + i[1] < distance[i[0]]:
distance[i[0]] = cost + i[1]
heapq.heappush(q, (distance[i[0]], i[0]))
return distance
if __name__ == "__main__":
n, m, c = map(int, sys.stdin.readline().split())
adj_list = [[] for _ in range(n+1)]
for _ in range(m):
x, y, z = map(int, sys.stdin.readline().split())
adj_list[x].append((y, z))
print(*solution(n, m, c, adj_list))
Adjacent matrix: 메모리 - 다익스트라. 노드가 비교적 많은 경우에도 사용 가능.
Adjacent list: 메모리 - 플로이드 워셜. 노드가 적은 경우 사용 가능.
서로소 집합 - 공통원소가 없는 두 집합
union(합집합) → 2개의 원소가 포함된 집합을 하나의 집합으로 합치는 연산
find(찾기) → 특정한 원소가 속한 집합이 어떤 집합인지 알려주는 연산
import sys
def find_parent(parent, x):
if parent[x] != x:
parent[x] = find_parent(parent, parent[x])
# return find_parent(parent, parent[x]) 와 비교했을 때 경로 압축.
return parent[x]
def union_parent(parent, a, b):
a = find_parent(parent, a)
b = find_parent(parent, b)
if a < b:
parent[b] = a
else:
parent[a] = b
# num of vertex: v, num of edge: e
v, e = map(int, sys.stdin.readline().split())
parent = list(range(v+1))
for i in range(e):
a, b = map(int, sys.stdin.readline().split())
union_parent(parent, a, b)
print('각 원소가 속한 집합: ', end='')
for i in range(1, v+1):
print(find_parent(parent, i), end='')
print()
print('parent table: ')
print(*parent)
트리 자료구조 사용. 노드의 개수가 V, V-1 개의 union 연산과 M개의 find 연산이 가능할 때 경로 압축 방법을 적용한 시간 복잡도
무방향 그래프 내에서 사이클 여부를 판단할 때 사용 할 수 있음.
import sys
def find_parent(parent, x):
if parent[x] != x:
# root node finding
parent[x] = find_parent(parent, parent[x])
return parent[x]
def union_parent(parent, a, b):
a = find_parent(parent, a)
b = find_parent(parent, b)
if a < b:
parent[b] = a
else:
parent[a] = b
# num of vertex: v, num of edge: e
v, e = map(int, sys.stdin.readline().split())
parent = list(range(v+1))
for i in range(e):
a, b = map(int, sys.stdin.readline().split())
if find_parent(parent, a) == find_parent(parent, b):
cycle = True
break
else:
union_parent(parent, a, b)
if cycle:
print("cycle")
else:
print("none")
신장트리
하나의 그래프가 있을 때 모든 노드를 포함하면서 사이클이 존재하지 않는 부분 그래프
최종적으로 신장트리에 포함되는 간선의 개수는 노드의 개수 -1개이다.
크루스칼 알고리즘 -
import sys
def find_parent(parent, x):
if parent[x] != x:
# root node finding
parent[x] = find_parent(parent, parent[x])
return parent[x]
def union_parent(parent, a, b):
a = find_parent(parent, a)
b = find_parent(parent, b)
if a < b:
parent[b] = a
else:
parent[a] = b
# num of vertex: v, num of edge: e
v, e = map(int, sys.stdin.readline().split())
parent = list(range(v+1))
edges = []
result = 0
for i in range(e):
a, b, cost = map(int, sys.stdin.readline().split())
edges.append((cost, a, b))
# 1 sort
edges.sort()
# 2
for edge in edges:
cost, a, b = edge
if find_parent(parent, a) != find_parent(parent, b):
union_parent(parent, a, b)
result += cost
print(result)
위상 정렬 -
방향 그래프의 모든 노드를 방향성에 거스르지 않도록 순서대로 나열하는 것
Indegree: 특정한 노드로 들어오는 간선의 개수
from collections import deque
import sys
v, e = map(int, sys.stdin.readline().split())
indegree = [0] * (v+1)
graph = [[] for i in range(v+1)]
for _ in range(e):
a, b = map(int, sys.stdin.readline().split())
graph[a].append(b)
indegree[b] += 1
def topology_sort():
result = []
q = deque([])
for i in range(1, v+1):
if indegree[i] == 0:
q.append(i)
while q:
now = q.popleft()
result.append(now)
for i in graph[now]:
indegree[i] -= 1
if indegree[i] == 0:
q.append(i)
print(*result)
topology_sort()
문제
import sys
def find_parent(parent, x):
if parent[x] != x:
parent[x] = find_parent(parent, parent[x])
return parent[x]
def union_parent(a, b, parent):
a = find_parent(parent, a)
b = find_parent(parent, b)
if a < b:
parent[b] = a
else:
parent[a] = b
n, m = map(int, sys.stdin.readline().split())
parent = list(range(n+1))
for _ in range(m):
o, a, b = map(int, sys.stdin.readline().split())
if o:
union_parent(a, b, parent)
else:
if find_parent(parent, a) == find_parent(parent, b):
print("YES")
else:
print("NO")
import sys
def find_parent(parent, x):
if parent[x] != x:
parent[x] = find_parent(parent, parent[x])
return parent[x]
def union_parent(parent, a, b):
a = find_parent(parent, a)
b = find_parent(parent, b)
if a < b:
parent[b] = a
else:
parent[a] = b
v, e = map(int, sys.stdin.readline().split())
parent = list(range(v+1))
edges = []
result = 0
for _ in range(e):
a, b, cost = map(int, sys.stdin.readline().split())
edges.append((cost, a, b))
edges.sort()
for edge in edges:
cost, a, b = edge
if find_parent(parent, a) != find_parent(parent, b):
union_parent(parent, a, b)
result += cost
last = cost
print(result - last)
import sys
import copy
from collections import deque
v = int(sys.stdin.readline())
indegree = [0] * (v+1)
graph = [[] for _ in range(v+1)]
time = [0] * (v+1)
for i in range(1, v+1):
data = list(map(int, sys.stdin.readline().split()))
time[i] = data[0]
for x in data[1:-1]:
indegree[i] += 1
def topology_sort():
result = copy.deepcopy(time)
q = deque([])
for i in range(1, v+1):
if indegree[i] == 0:
q.append(i)
while q:
now = q.popleft()
for i in graph[now]:
result[i] = max(result[i], result[now] + time[i])
indegree[i] -= 1
if indegree[i] == 0:
q.append(i)
print(*(result[1:]))
topology_sort()