오늘 부터는 날짜가 아니라 주제를 제목으로 하려고 한다. 왜냐하면 날짜가 제목이어서 헷갈리거나 생각이 잘 안나는 내용들을 찾기가 좀 어려웠다...
오늘 배운건 최소신장 트리이다.
- 최소 신장 트리(Minimum Spanning Tree, MST)란?
- 최소 스패닝 트리 라고도 많이 불린다.
- 주어진 그래프의 모든 정점을 지나면서, 간선의 가중치 합이 최소가 되는 트리
- 그래프의 모든 정점을 포함하고
- 그래프의 일부 간선을 포함하며
- 사이클이 없는 (트리의 특징)
- 위 특징을 가진 서브 그래프 중 간선의 가중치 합이 최소인 트리
- 원본 그래프 간선에 가중치가 부여되어 있음
- 간선의 방향 여부에 따라 구하는 방법이 달라짐
- 코딩테스트에서는 간선의 방향이 없는, 무방향 그래프에 대해서만 나온다
- 정점이 n 개인 그래프의 최소 신장 트리는, n - 1 개의 간선을 가져야 한다 (트리의 특징)
주요 알고리즘
- 프림 (Prim’s Algorithm)
- 작동 순서
- 임의의 정점을 시작으로 선택한다.
- 해당 정점과 연결된 간선을 탐색 대상에 추가한다.
- 탐색 대상에서 가장 가중치가 낮은 간선을 뽑는다. 간선의 끝 점이 아직 방문하지 않았다면, 그 간선을 추가한다. 방문했다면, 방문하지 않은 간선이 나올 때까지 간선을 뽑는다.
- 새로 추가된 정점과 연결된 간선을 탐색 대상에 추가한다.
- 3 ~ 4번 과정을 (전체 정점 - 1) 개의 간선이 트리에 추가될 때까지 반복한다.
- 크루스칼 (Kruskal’s Algorithm)
- 작동 순서
- 모든 간선들을 가중치에 대해 오름차순으로 정렬한다.
- 앞에서부터, 간선을 트리에 추가했을 때, 사이클이 생기지 않는다면 그 간선을 트리에 추가한다.
- 사이클이 생긴다면 그대로 넘어간다 (간선을 트리에 추가하지 않는다)
- 2번 과정을 (전체 정점 - 1) 개의 간선이 트리에 추가될 때까지 반복한다.
- 작동 순서
분리 집합 (Union-Find / Disjoint-Set)
그래프가 유동적으로 변할 때(간선이 추가/삭제 될 때), 점들 간의 연결 여부를 빠르게 확인할 수 있어, 크루스칼 알고리즘 구현 외에도 많이 사용된다. 부모 정점을 찾는 find 함수와,서로 다른 부모 정점을 가지는 집합을 합치는 Union 함수로 이루어져 있다.
난 오늘 크루스칼을 제대로 이해하지 못했다. 그래서 강의를 보고 구글링을 한 결과 지금은 어느정도 이해하기 시작했다.
그러나 문제풀때는 그러하지 못해서 프림으로 문제를 해결했다.
1. 네트워크 연결 https://www.acmicpc.net/problem/1922
1922번: 네트워크 연결
이 경우에 1-3, 2-3, 3-4, 4-5, 4-6을 연결하면 주어진 output이 나오게 된다.
www.acmicpc.net
import heapq
def network():
N = int(input())
M = int(input())
edge = [[] for _ in range(N + 1)]
for _ in range(M):
a, b, c = map(int, input().split())
edge[a].append((c, b))
edge[b].append((c, a))
heap = [(0, 1)]
rs = 0
chk = [False] * (N + 1)
while heap:
cost, node = heapq.heappop(heap)
if not chk[node]:
chk[node] = True
rs += cost
for next_cost, next_node in edge[node]:
heapq.heappush(heap, (next_cost, next_node))
return rs
평범하게 프림으로 해결했다. 프림으로 문제를 풀다보니 기본코드를 알면 그 코드만으로 문제가 거의 풀린다는 것이었다. 그래서 기본코드를 꼭 외워야 한다.
2. 도시 분할 계획 https://www.acmicpc.net/problem/1647
1647번: 도시 분할 계획
첫째 줄에 집의 개수 N, 길의 개수 M이 주어진다. N은 2이상 100,000이하인 정수이고, M은 1이상 1,000,000이하인 정수이다. 그 다음 줄부터 M줄에 걸쳐 길의 정보가 A B C 세 개의 정수로 주어지는데 A번
www.acmicpc.net
import heapq
def city():
N,M = map(int, input().split())
edge = [[] for _ in range(N+1)]
for i in range(M):
a,b,c = map(int, input().split())
edge[a].append([c,b])
edge[b].append([c,a])
heap = []
heap.append((0,1))
chk = [False]*(N+1)
rs = []
while heap:
w, each_node = heapq.heappop(heap)
if chk[each_node] == False:
chk[each_node] = True
rs.append(w)
if len(rs) == N:
break
for next_cost, next_edge in edge[each_node]:
heapq.heappush(heap, (next_cost,next_edge))
return sum(rs) - max(rs)
print(city())
*강의 참조
https://www.youtube.com/watch?v=nZ4RTuoHS_Y
크루스칼이 제대로 이해가 되지 않아 MST에 대한 자료를 찾던 중 유튜브 강의가 있길래 참고했는데 쉽게 설명하고 프림에 대해 잘 설명이 되어 있어서 이 영상을 본 후 프림으로 수월하게 해결했다. 리스트를 0번 부터가 아닌 1번부터 사용해야 하니까 N+1로 해줬고 즉 [0]번은 사용되지 않는다.
3. 행성 연결 https://www.acmicpc.net/problem/16398
16398번: 행성 연결
홍익 제국의 중심은 행성 T이다. 제국의 황제 윤석이는 행성 T에서 제국을 효과적으로 통치하기 위해서, N개의 행성 간에 플로우를 설치하려고 한다. 두 행성 간에 플로우를 설치하면 제국의 함
www.acmicpc.net
import heapq
def planet():
N = int(input())
edge = [[] for _ in range(N+1)]
chk = [False] * (N+1)
rs = 0
for _ in range(N):
weights = list(map(int, input().split()))
for idx, weight in enumerate(weights):
if weight != 0:
edge[_+1].append((weight, idx+1))
heap = [(0, 1)]
while heap:
w, each_node = heapq.heappop(heap)
if chk[each_node] == False:
chk[each_node] = True
rs += w
for next_edge in edge[each_node]:
if not chk[next_edge[1]]:
heapq.heappush(heap, next_edge)
return rs
print(planet())
3번과 거의 동일하나 차이가 있다면 input방식이 좀 다르다. 입력값을 weights에 list로 저장했고 enumerate를 사용해서 index와 값을 다 확인하게 했다. edge[_+1].append((weight, idx+1)) 이 부분은 위와 마찬가지로 1번 행성부터 쓰이니까 인덱스 값에 1을 더했고 다음 행성의 인덱스도 1을 더해줬다.
오늘은 문제자체는 꽤 수월하게 풀었다. 다만 크루스칼이나 Union-Find에 대해 이해가 잘 되지 않아 꽤나 고생했다. 앞으로도 개념적으로 어려운것들이 계속 나올텐데 다른건 몰라도 코드를 직관적이로 봤을때 이해하는 능력이 빨리 길러졌으면 좋겠다고 수없이 생각했다...
*드림핵
보안에 관심이 있는 편이라 시작을 어떻게 할수 있냐는 질문을 했고 해당 사이트를 추천해주셨다. 지금 과정이 벅차 지금 당장은 무리이고 이 과정이 끝나면 시작해봐야겠다.
'알고리즘' 카테고리의 다른 글
다익스트라&플로이드&동적계획법 (0) | 2024.02.28 |
---|---|
알고리즘 day 6 & 7 (2) | 2024.02.16 |
알고리즘 day 5 & test (2) | 2024.02.14 |
일주일간의 회고 (0) | 2024.02.11 |
알고리즘 day4 (1) | 2024.02.08 |