[파이썬] 백준 2887 : 행성터널 (플레5)
2887번: 행성 터널
첫째 줄에 행성의 개수 N이 주어진다. (1 ≤ N ≤ 100,000) 다음 N개 줄에는 각 행성의 x, y, z좌표가 주어진다. 좌표는 -109보다 크거나 같고, 109보다 작거나 같은 정수이다. 한 위치에 행성이 두 개 이
www.acmicpc.net
문제
풀이
0. 방향성 생각
- 지문만 읽으면 MST 문제임을 바로 알 수 있다.
- 이전 골드 문제와는 다르게 입력 크기가 10만이라서 모든 간선 정보를 받는 경우에는 n(n-1)/2 = 5*10^11이다.
- 특정 차원을 선택해서 위치별로 정렬시키고, 인접한 node끼리만 간선 정보에 추가해준다. (증명은 질문 게시판에)
1. 입력
from collections import defaultdict as dd
import sys
input = lambda : sys.stdin.readline().rstrip()
n = int(input())
info = [[] for _ in range(3)]
for i in range(n):
loc = tuple(map(int,input().split()))
for j in range(3):
info[j].append((loc[j],i))
inf = 1e10
edges = dd(lambda:inf)
for j in range(3):
info[j].sort()
for i in range(n-1):
p1,idx1 = info[j][i]
p2,idx2 = info[j][i+1]
edges[(idx1,idx2)] = min(abs(p1-p2),edges[(idx1,idx2)])
edges = list(edges.items())
edges.sort(key=lambda x:-x[1])
- dd에다가 인접 노드를 연결하는 경우 최소값으로 갱신해준다.
2. 함수 정의
parent = list(range(n))
def find(x):
if x != parent[x]:
parent[x] = find(parent[x])
return parent[x]
def union(x,y):
parent[max(x,y)] = min(x,y)
- union에는 항상 루트 노드를 넣고 작은 노드를 부모로 삼는다.
- 안에서 find를 해서 해도 되지만, union을 하는 조건문이 바깥에 있으면 중복해서 찾기 때문에 비효율적이다.
3. 출력
count,answer = 0,0
while count < n-1:
nodes,cost = edges.pop()
a,b = nodes
pa,pb = find(a),find(b)
if pa != pb:
union(pa,pb)
answer += cost
count += 1
print(answer)
- n-1개를 연결했으면 루프를 탈출해준다.
- edges에서 만든 정보들을 pop시켜서 find를 통해 부모를 찾고, 다른 집합에 속해있으면 연결해준다.
전체코드
from collections import defaultdict as dd
import sys
input = lambda : sys.stdin.readline().rstrip()
n = int(input())
info = [[] for _ in range(3)]
for i in range(n):
loc = tuple(map(int,input().split()))
for j in range(3):
info[j].append((loc[j],i))
inf = 1e10
edges = dd(lambda:inf)
for j in range(3):
info[j].sort()
for i in range(n-1):
p1,idx1 = info[j][i]
p2,idx2 = info[j][i+1]
edges[(idx1,idx2)] = min(abs(p1-p2),edges[(idx1,idx2)])
edges = list(edges.items())
edges.sort(key=lambda x:-x[1])
parent = list(range(n))
def find(x):
if x != parent[x]:
parent[x] = find(parent[x])
return parent[x]
def union(x,y):
parent[max(x,y)] = min(x,y)
count,answer = 0,0
while count < n-1:
nodes,cost = edges.pop()
a,b = nodes
pa,pb = find(a),find(b)
if pa != pb:
union(pa,pb)
answer += cost
count += 1
print(answer)
코멘트
증명은 모르겠고, 삼각 부등식 생각 + 직관으로 가까운거만 넣어서 풀긴 했는데 아직 와닿지는 않는..
유니온 시킬 때, 루트노드 넣어야되는데 그냥 노드 넣어서 답이 안나와서 한참 해맸음. 웃긴건 테케랑 반례는 맞음 ㅠ
'Algorithm > Graph' 카테고리의 다른 글
[파이썬] 백준 2021 : 최소 환승 경로 (골드2) (0) | 2023.12.17 |
---|---|
[파이썬] 백준 5213 : 과외맨 (골드2) (0) | 2023.12.17 |
[파이썬] 백준 16441 : 아기돼지와 늑대 (골드3) (0) | 2023.12.17 |
[파이썬] 백준 16933 : 벽 부수고 이동하기 3 (골드1) (0) | 2023.12.16 |
[파이썬] 백준 14442 : 벽 부수고 이동하기 2 (골드3) (0) | 2023.12.15 |
[파이썬] 백준 1414 : 불우이웃돕기 (골드3) (0) | 2023.12.13 |
[파이썬] 백준 1774 : 우주신과의 교감 (골드3) (0) | 2023.12.13 |
댓글