백준 11724번 연결 요소의 개수
서론
BFS를 정리해보려고 문제 몇 개를 푸는 중에 BFS 문제가 Union Find로도 풀릴 것 같아 Union Find를 적용해봤더니 풀려서 기록해봅니다.
제한 조건
- 시간제한 3초
기본적으로 1초에 1억 번 연산한다고 생각하고 풉니다. BFS의 경우 O(V+E)의 시간복잡도를 가지는데 여기서 V = 1000, E = V^2이므로 V+E는 약 1,000,000이고 3초의 시간이면 충분합니다. 실버단계 문제라서 시간을 여유롭게 준걸까요? 3초까진 필요 없을 것 같습니다.
- 메모리 제한 512MB
그래프를 선언해놓고 풀면 약 E의 두 배에 해당하는 공간이 필요한데(방향성이 없으므로 양 시작 노드에 다 들어가므로) 2,000,000개의 파이썬 인트형 28byte 면 56MB이니 메모리 제한도 충분합니다.
BFS
기본적으로 그래프를 한 번씩 다 탐색해보면서 연결된 애들은 visited 리스트에 같은 숫자를 넣었습니다.
import sys
from collections import deque as dq
input = sys.stdin.readline
N, M = map(int, input().split())
graph = {}
for i in range(1, N+1):
graph[i] = []
for i in range(M):
a, b = map(int, input().split())
graph[a].append(b)
graph[b].append(a)
cnt = 0
visited = [0]*(N+1)
for i in range(1, N+1):
if visited[i] == 0:
que = dq()
que.append(i)
cnt += 1
visited[i] = cnt
while que:
base = que.popleft()
for next in graph[base]:
if visited[next] == 0:
que.append(next)
visited[next] = cnt
print(max(visited))
Union Find
연결된 요소를 찾는다는 것 자체가 Union Find 느낌이 와서 적용해봤는데 잘 풀리는 것 같습니다. 주의할 점은 지금까지 Union Find에서 parent를 구할 때 parent 리스트는 같은 그룹이라고 같은 숫자가 쓰여있는 것이 아니기 때문에 문제에 맞게 바꾸기 위해서 한 번 더 최신화를 해주었습니다.
import sys
sys.setrecursionlimit(10**6)
input = sys.stdin.readline
N, M = map(int, input().split())
parent = [i for i in range(N+1)]
def find(a):
if parent[a] == a:
return a
parent[a] = find(parent[a])
return parent[a]
def union(a, b):
a = find(a)
b = find(b)
if a == b:
return
parent[a] = b
for __ in range(M):
a, b = map(int, input().split())
union(a, b)
# 이 부분이 최신화를 해주는 부분. 이렇게 한번더 정리해주면 같은 그룹은 같은 숫자를 가지는 parent리스트가 됩니다.
for i in range(N+1):
parent[i] = find(i)
print(len(set(parent[1:])))
생각
그럼 뭐가 더 효율적일지를 생각해보면
- 시간복잡도 측면에서는 BFS는 O(V+E)를 가지고 Union Find는 정확히 설명할 순 없지만 두 개의 루프가 V, E로 돌아가고 루프 안의 Union과 Find 함수는 일반적으로 상수라고 알려져 있으므로 O(V+E)쯤이 될 것 같습니다. 시간복잡도 측면에선 거의 비슷하겠네요.
- 공간복잡도 측면에서는 BFS는 그래프를 선언해놓고 하면 O(V+E)의 공간복잡도를 가지게 되지만 Union Find는 V에 해당하는 parent 리스트만 있으면 E에 해당하는 선언은 할 필요가 없습니다. 그래서 공간복잡도 상으로는 Union Find가 이득일 것 같습니다.
BOJ내 다른 글 보기
댓글남기기