문제 <행성 터널>
때는 2040년, 이민혁은 우주에 자신만의 왕국을 만들었다. 왕국은 N개의 행성으로 이루어져 있다. 민혁이는 이 행성을 효율적으로 지배하기 위해서 행성을 연결하는 터널을 만들려고 한다.
행성은 3차원 좌표위의 한 점으로 생각하면 된다. 두 행성 A(xA, yA, zA)와 B(xB, yB, zB)를 터널로 연결할 때 드는 비용은 min(|xA-xB|, |yA-yB|, |zA-zB|)이다.
민혁이는 터널을 총 N-1개 건설해서 모든 행성이 서로 연결되게 하려고 한다. 이때, 모든 행성을 터널로 연결하는데 필요한 최소 비용을 구하는 프로그램을 작성하시오.
입력
- 첫째 줄에 행성의 개수 N이 주어진다. (1 ≤ N ≤ 100,000)
- 다음 N개 줄에는 각 행성의 x, y, z좌표가 주어진다. 좌표는 -109보다 크거나 같고, 109보다 작거나 같은 정수이다.
- 한 위치에 행성이 두 개 이상 있는 경우는 없다.
출력
- 첫째 줄에 모든 행성을 터널로 연결하는데 필요한 최소 비용을 출력한다.
풀이
- 크루스칼 알고리즘을 이용하여 문제를 풀어야겠다고 생각했다.
- 모든 행성을 터널로 연결하는데 필요한 최소 비용을 구하기 위해, 행성 두 개를 묶어 조합을 생성해 보았다.
- 형성된 조합에서 터널을 구해 비용을 key로 오름차순 정렬을 한다면, 크루스칼 알고리즘이 잘 동작할 것이라 생각했다.
import sys
from itertools import combinations
input_data = sys.stdin.readline
def find_parent(parent, x):
if parent[x] != x:
parent[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 = int(input_data())
parent = [i for i in range(N+1)]
edges = []
planet = [0]
num = [i for i in range(1, N+1)]
for i in range(1, N+1):
X, Y, Z = map(int, input_data().split())
planet.append((X, Y, Z))
combi = list(combinations(num, 2))
for i in combi:
min_cost = min(abs(planet[i[0]][0]-planet[i[1]][0]), abs(planet[i[0]][1]-planet[i[1]][1]), abs(planet[i[0]][2]-planet[i[1]][2]))
edges.append((min_cost, i[0], i[1]))
result = 0
edges.sort()
for edge in edges:
cost, a, b = edge[0], edge[1], edge[2]
if find_parent(parent, a) != find_parent(parent, b):
union(parent, a, b)
result += cost
print(result)
하지만, 행성 각각을 연결하는 터널을 모두 구하다 보니 메모리 초과가 일어났다.
다시 생각해보니 모든 터널을 구할 필요 없이 모든 행성을 연결하는 터널은 N-1개만 있으면 됐다. (N개의 노드가 존재할 때, N-1개의 간선 연결만 있으면 모든 노드가 연결됨)
따라서, 3개의 축 각각을 기준으로 오름차순 정렬을 해 최소 비용을 가지는 터널을 구하여 문제를 해결하였다.
import sys
input_data = sys.stdin.readline
def find_parent(parent, x):
if parent[x] != x:
parent[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 = int(input_data())
parent = [i for i in range(N+1)]
edges = []
planet = []
for i in range(0, N):
X, Y, Z = map(int, input_data().split())
planet.append((X, Y, Z, i+1))
for i in range(3):
planet.sort(key=lambda x:x[i])
for j in range(1, N):
edges.append((abs(planet[j-1][i] - planet[j][i]), planet[j-1][3], planet[j][3]))
result = 0
edges.sort()
for edge in edges:
cost, a, b = edge[0], edge[1], edge[2]
if find_parent(parent, a) != find_parent(parent, b):
union(parent, a, b)
result += cost
print(result)
알고리즘을 올바르게 설계하기 위해 메모리 초과를 고려해야겠다.
N개의 노드가 존재할 때 N-1개의 간선 연결만 있다면 모든 노드가 연결될 수 있다!
2887번: 행성 터널
첫째 줄에 행성의 개수 N이 주어진다. (1 ≤ N ≤ 100,000) 다음 N개 줄에는 각 행성의 x, y, z좌표가 주어진다. 좌표는 -109보다 크거나 같고, 109보다 작거나 같은 정수이다. 한 위치에 행성이 두 개 이
www.acmicpc.net
'CS > 알고리즘' 카테고리의 다른 글
[이코테] 다이나믹 프로그래밍 - 평범한 배낭 (0) | 2023.06.08 |
---|---|
[이코테] 그래프 이론 - 최종 순위 (0) | 2023.05.18 |
[이코테] 다이나믹 프로그래밍 - 퇴사 (0) | 2023.04.03 |
[백준] 이진 탐색 - 1477 휴게소 세우기 (0) | 2023.03.23 |
Python 집합(Set) 자료형 (0) | 2023.03.16 |