개발/백준

백준 1774: 우주신과의 교감 (Python)

센솔 2024. 3. 28. 00:03

문제

황선자씨는 우주신과 교감을 할수 있는 채널러 이다. 하지만 우주신은 하나만 있는 것이 아니기때문에 황선자 씨는 매번 여럿의 우주신과 교감하느라 힘이 든다. 이러던 와중에 새로운 우주신들이 황선자씨를 이용하게 되었다.

 

하지만 위대한 우주신들은 바로 황선자씨와 연결될 필요가 없다. 이미 황선자씨와 혹은 이미 우주신끼리 교감할 수 있는 우주신들이 있기 때문에 새로운 우주신들은 그 우주신들을 거쳐서 황선자 씨와 교감을 할 수 있다.

 

우주신들과의 교감은 우주신들과 황선자씨 혹은 우주신들 끼리 이어진 정신적인 통로를 통해 이루어 진다. 하지만 우주신들과 교감하는 것은 힘든 일이기 때문에 황선자씨는 이런 통로들이 긴 것을 좋아하지 않는다. 왜냐하면 통로들이 길 수록 더 힘이 들기 때문이다.

 

또한 우리들은 3차원 좌표계로 나타낼 수 있는 세상에 살고 있지만 우주신들과 황선자씨는 2차원 좌표계로 나타낼 수 있는 세상에 살고 있다. 통로들의 길이는 2차원 좌표계상의 거리와 같다.

 

이미 황선자씨와 연결된, 혹은 우주신들과 연결된 통로들이 존재한다. 우리는 황선자 씨를 도와 아직 연결이 되지 않은 우주신들을 연결해 드려야 한다. 새로 만들어야 할 정신적인 통로의 길이들이 합이 최소가 되게 통로를 만들어 “빵상”을 외칠수 있게 도와주자.

해설

 문제가 쓸 데 없이 설명이 길다. 우주신은 '정점', 통로는 '간선' 으로 생각을 해본다면 이 문제는 "모든 정점을 잇는 경로를 구하되, 비용이 최소가 되게 하라" 는 말이 된다. 모든 정점을 잇는 최소 비용의 경로.. 익숙하다. 최소 스패닝 트리(MST)유니온 파인드개념을 이용해서 풀면 되겠다.

 

다만 이 문제에서는 특이하게도 간선에 대한 정보를 제공하지 않는다. 정점의 좌표 X, Y가 입력될 뿐이다. 따라서 우리는 모든 정점 간의 간선을 찾을 필요가 있다. 이 점을 고려하여 문제를 푼 과정을 정리해보자면,

 

  • 모든 좌표들을 입력받아 리스트에 저장한다
  • 좌표 간의 거리를 구하는 함수를 정의한다
  • 모든 정점들에 대하여 정점 a와 b 사이의 거리(비용)을 구한다.
    • 이후 [비용, a, b] 형태로 edge 리스트에 저장한다.

여기까지 완료했다면 이 문제는 '최소 스패닝 트리 문제' (백준 1197번, 해설)와 완벽하게 같아진다.

소수점 둘 째 자리까지 반올림한 포맷을 사용하여 출력한다는 점만 유의해서 나머지를 구현하면 된다.

 

 

코드

import math
import sys

input = sys.stdin.readline
sys.setrecursionlimit(1000000)


def get_distance(x1, y1, x2, y2):
    return math.sqrt((x1-x2)**2 + (y1-y2)**2)

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

def union(parent, v1, v2):
    v1 = find_parent(parent, v1)
    v2 = find_parent(parent, v2)

    if v1 < v2:
        parent[v2] = v1
    else:
        parent[v1] = v2

N, M = map(int, input().split())
coords = []
edges = []
parent = [0] + list(range(1, N+1))

for i in range(N):
    X, Y = map(int, input().split())
    coords.append([X,Y])
    
for v1 in range(N):
    for v2 in range(v1+1, N):
        v1_x, v1_y = coords[v1]
        v2_x, v2_y = coords[v2]
        dist = get_distance(v1_x, v1_y, v2_x, v2_y)
        
        edges.append((dist, v1, v2))
edges = list(sorted(edges, key = lambda x: x[0]))

for i in range(M):
    v1, v2 = map(int, input().split())
    v1, v2 = v1-1, v2-1 # 인덱싱 처리
    union(parent, v1, v2)

total_cost = 0
for i in range(len(edges)):
    dist, v1, v2 = edges[i]
    if find_parent(parent, v1) != find_parent(parent, v2):
        union(parent, v1, v2)
        total_cost += dist

print("{:.2f}".format(round(total_cost, 2)))