티스토리 뷰

알고리즘

[이코테] 그래프

바랄 희 2022. 9. 28. 14:34

✅ 서로소 집합 표현

- union / find 연산 수행

- 부모 테이블을 따로 둬서 부모 노드를 기록

ex) union 1,4

4 의 부모가 4에서 1로 변경

 

union 2, 3

3의 부모가 3에서 2로 변경

 

union 2,4

4의 부모는 더 작은 2로 그대로 둠

 

⌨️ 소스코드

def find_parent(parent, x):
	if parent[x] != x:
    	return find_parent(parent, parent[x])
    return 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,input().split())
    parent = [0]*(v+1)
    
    # 부모를 자기 자신으로 초기화
    for i in range(1, v+1):
    	parent[i] = i
    
    # union 연산 수행
    for i in range(e):
    	a, b = map(int,input().split())
        union_parent(parent, a, b)

 

⏰ 시간복잡도

- 노드의 루트 노드를 찾기 위해서 거슬러 올라가야 하기 때문에 O(V) 의 시간이 소요될 수 있음. 

 

경로 압축을 이용해서 최적화 가능.

find 를 재귀적으로 호출한 뒤에 부모 테이블 값을 갱신하는 기법.

 

def find_parent(parent, x):
	if parent[x] != x:
    	parent[x] = find_parent(parent, parent[x])
    return parent[x]

 

이렇게 갱신할 경우, 부모 테이블의 접근을 통해서 바로 루트 노드를 알 수 있음.

 

방향성이 없는 경우, 사이클 판별도 가능!

 

각 간선을 통해서 루트 노드를 확인하고, 루트 노드가 서로 다르다면 union 연산을 수행하고 루트 노드가 같다면 사이클이 발생한 것으로 판별.

 

for i in range(e):
	a, b = map(int,input().split())
    if find_parent(parent, a) == find_parent(parnet, b):
    	cycle = True
        break
    else:
    	union_parent(parent, a, b)

 

✅ 신장 트리

- 하나의 그래프가 있을 때 모든 노드를 포함하면서 사이클이 존재하지 않는 부분 그래프

ex) N개의 도시가 존재하는 상황에서 두 도시 사이에 도로를 놓아 전체 도시에 연결될 수 있도록 할 때

 

✅ 크루스칼 알고리즘

- 모든 간선을 정렬하고, 거리가 짧은 간선부터 집합에 포함시킴

- 사이클을 발생시킬 수 있는 간선은 포함시키지 않음.

- 같은 집합에 포함되지 않는 경우 (사이클을 발생시키지 않는 경우) 사이클 연산을 수행하도록 함

 

⌨️ 소스코드

#루트 노드 찾기
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
 
 # 중략 . . .
 
edges = []
result = 0

for i in range(1, v+1):
	parent[i] = i
    
for _ in range(3):
	a, b, cost = map(int, input().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

⏰ 시간복잡도 O(E logE)

간선을 정렬하는 데에 가장 시간이 오래 걸림

 


 

✅ 위상 정렬

- 방향 그래프의 모든 노드를 방향성에 거스르지 않도록 순서대로 나열하는 것

1. 진입차수가 0인 노드를 큐에 넣음

2. 큐가 빌 때까지 큐에서 원소를 꺼내 해당 노드에서 출발하는 간선을 그래프에서 제거 / 새롭게 진입차수가 0이 된 노드를 큐에 넣음

 

⌨️ 소스코드

from collections import deque

v, e = map(int,input().split())
indegree = [0] * (v+1)
graph = [[] for i in range(v+1)]

for _ in range(e):
	a, b = map(int,input().split())
    graph[a].append(b) #a 에서 b 로 이동할 수 있다는 뜻!
    indegree[b] +=1

def topology_sort():
	result = []
    q = deque()
    
    for i in range(1, v+1): #진입차수가 0인 노드를 큐에 삽입
    	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)

⏰ 시간복잡도 O(V+E)

- 간선을 차례대로 제거해야 함

 


🧐 팀 결성

# p.298 팀 결성

def find_parent(parent, x):
    if parent[x]!= x:
        find_parent(parent, parent[x])
    return parent[x]

def union(parent, a, b):
    a = find_parent(parent, a)
    b = find_parent(parent, b)
    if a < b:
        parent[b] = a
    else:
        parent[a] = b

n,m = map(int,input().split())

parent = [0 for _ in range(n+1)]

for i in range(n+1):
    parent[i] = i

for _ in range(m):
    cal, a, b = map(int,input().split())
    if cal == 0:
        union(parent, a, b)
    else:
        if find_parent(parent, a) == find_parent(parent, b):
            print("YES")
        else:
            print("NO")

🧐 도시 분할 계획

# p.300 도시 분할 계획

'''
n개의 집과 m개의 길
2개로 분할하기
'''

def find_parent(parent, x):
    if parent[x] != 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

n,m = map(int,input().split())
parent = [0 for _ in range(n+1)]

for i in range(n+1):
    parent[i] = i

edges = []

for _ in range(m):
    a, b, c = map(int,input().split())
    edges.append(c, a, b)

edges.sort()
last = 0
result = 0

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)

두개의 도시로 만들기 위해서 크루스칼 알고리즘을 이용해 가장 비용이 낮은 간선부터 더하고, 가장 큰 값인 last 를 빼주면 두개의 신장 트리를 얻을 수 있다

 

'알고리즘' 카테고리의 다른 글

[프로그래머스] Lv1 문제 풀기 -1  (0) 2022.11.25
백준 1421 파이썬  (0) 2022.10.14
[이코테] 이진 탐색  (0) 2022.08.29
[이코테] 정렬  (0) 2022.08.25
백준 1012 유기농 배추 파이썬  (0) 2022.08.17
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/06   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함