
난이도 : 골드 3
유형 : 최소신장트리 (MST)
출처 : https://www.acmicpc.net/problem/1774
황선자씨는 우주신과 교감을 할수 있는 채널러 이다.
하지만 우주신은 하나만 있는 것이 아니기때문에 황선자 씨는 매번 여럿의 우주신과 교감하느라 힘이 든다.
이러던 와중에 새로운 우주신들이 황선자씨를 이용하게 되었다.
하지만 위대한 우주신들은 바로 황선자씨와 연결될 필요가 없다. 이미 황선자씨와 혹은 이미 우주신끼리 교감할 수 있는 우주신들이 있기 때문에 새로운 우주신들은 그 우주신들을 거쳐서 황선자 씨와 교감을 할 수 있다.
우주신들과의 교감은 우주신들과 황선자씨 혹은 우주신들 끼리 이어진 정신적인 통로를 통해 이루어 진다. 하지만 우주신들과 교감하는 것은 힘든 일이기 때문에 황선자씨는 이런 통로들이 긴 것을 좋아하지 않는다. 왜냐하면 통로들이 길 수록 더 힘이 들기 때문이다.
또한 우리들은 3차원 좌표계로 나타낼 수 있는 세상에 살고 있지만 우주신들과 황선자씨는 2차원 좌표계로 나타낼 수 있는 세상에 살고 있다. 통로들의 길이는 2차원 좌표계상의 거리와 같다.
이미 황선자씨와 연결된, 혹은 우주신들과 연결된 통로들이 존재한다.
우리는 황선자 씨를 도와 아직 연결이 되지 않은 우주신들을 연결해 드려야 한다. 새로 만들어야 할 정신적인 통로의 길이들이 합이 최소가 되게 통로를 만들어 “빵상”을 외칠수 있게 도와주자.
첫째 줄에 우주신들의 개수 () 이미 연결된 신들과의 통로의 개수 ()가 주어진다.
두 번째 줄부터 개의 줄에는 황선자를 포함하여 우주신들의 좌표가 , ()가 주어진다. 그 밑으로 개의 줄에는 이미 연결된 통로가 주어진다. 번호는 위의 입력받은 좌표들의 순서라고 생각하면 된다. 좌표는 정수이다.
첫째 줄에 만들어야 할 최소의 통로 길이를 소수점 둘째 자리까지 반올림하여 출력하라.
문제에 군더더기들이 조금 있지만 읽어봤을 때 결국에는 모든 간선들을 잇게하는
예제 1을 보면
4 1 # 우주신들의 개수 = 노드 개수 4, 이미 연결된 신들과의 통로의 개수 = 연결되어있는 간선의 개수 1
1 1 # 2차원 평면에서 노드의 좌표
3 1 # 2차원 평면에서 노드의 좌표
2 3 # 2차원 평면에서 노드의 좌표
4 3 # 2차원 평면에서 노드의 좌표
1 4 # 이미 연결된 통로
이를 그림으로 나타내면

이 모습이다. 우주신 이라고 불리는 노드들이 2차원 좌표 평면에 4개 존재하고,
입력받은 순서대로의 번호 중 1번 노드와 4번노드가 연결된 꼴.
여기서 우리의 할일은 아직 연결되지 않은 노드들을 연결 시켜 줄건데 최소한의 비용을 들여 새로 이어주어야 한다.
추가로 연결한 간선들의 거리 합을 소수 둘째 자리까지 출력해주면 된다.
import sys
import math
input = sys.stdin.readline
N, M = map(int,input().split())
# 우주신들 좌표 저장
gods = [None] # 인덱스 1부터 맞추기 위해 앞에 더미 하나
for _ in range(N):
X, Y = map(int,input().split())
gods.append((X,Y))
edges = []
for i in range(1, N+1):
x1, y1 = gods[i]
for j in range(i+1, N+1):
x2, y2 = gods[j]
dist = math.hypot(x1 - x2, y1 - y2) # hypot 은 hypotenuse(빗변) 의 줄임말, math.hypot(x, y) 는 x,y를 밑변, 높이로 하는 직각삼각형의 빗변 길이를 계산해주는 함수.
edges.append((dist, i, j))
# 유니온 파인드
# parent, rank 초기화 (더미 0번 포함)
parent = []
rank = []
for i in range(N+1):
parent.append(i)
rank.append(0)
def find(x):
if parent[x] != x:
parent[x] = find(parent[x])
return parent[x]
def union(a, b):
ra,rb = find(a), find(b)
if ra == rb:
return False
if rank[ra] < rank[rb]:
parent[ra] = rb
elif rank[ra] > rank[rb]:
parent[rb] = ra
else:
parent[rb] = ra
rank[ra] += 1
return True
# 이미 연결되어 있는 통로는 바로 union 처리
for _ in range(M):
a, b = map(int,input().split())
union(a,b)
# 크루스칼로 최소 거리 합 구하기
edges.sort()
ans = 0.0
for w, u, v in edges:
if union(u,v):
ans += w
print(f"{ans:.2f}")
모든 점 간의 거리 배열을 만들 때 저번에는 c^2 = a^2 + b^2 피타고라스의 정리를 직접 활용해서 구했지만 이번에는 math 라이브러리에 hypot 함수를 써서 구했다.
math.hypot이는 파이썬 기본 내장 라이브러리의 hypot이라는 함수인데 여기서 hypot은 hypotenuse(빗변) 의 줄임말이다.
즉 math.hypot(x, y) 는 x,y를 밑변, 높이로 하는 직각삼각형의 빗변 길이를 계산해주는 함수이다.