백준 2887 행성 터널 java
2021. 1. 21. 16:13
문제 소개 www.acmicpc.net/problem/2887
내용
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보다 작거나 같은 정수이다. 한 위치에 행성이 두 개 이상 있는 경우는 없다.
출력
첫째 줄에 모든 행성을 터널로 연결하는데 필요한 최소 비용을 출력한다.
해결 방법 - 1
아이디어
- n-1 개의 간선을 선택해서 최소 비용 신장 트리를 만드는 것과 동일한 문제로 생각된다.
- 최소 비용 신장 트리를 만드는 데 사용하는 알고리즘은 크루스칼과 프림이 있다.
- 크루스칼의 시간복잡도는 eloge 이다.
- 정점의 수가 100,000이다. 고로 edge의 총 개수는 정점 제곱과 비례하니까 100,000 제곱개 임을 알 수 있었다.
- 크루스칼로 풀면 시간 초과가 날 것 같다고 생각했다.
- 그러면 프림으로 도전해보자. 프림의 알고리즘의 시간복잡도는 n^2 이니까. 그나마 크루스칼 보다 빠르다고 생각했다.
실제 풀이 코드
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int planetCount = Integer.parseInt(br.readLine());
List<Planet> planets = new ArrayList<>();
for (int i = 0; i < planetCount; i++) {
String[] split = br.readLine().split(" ");
planets.add(new Planet(i, Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2])));
}
boolean[] visited = new boolean[planetCount];
Queue<Planet> queue = new LinkedList<>();
queue.add(planets.get(0));
int answer = 0;
while(!queue.isEmpty()){
Planet current = queue.poll();
visited[current.index] = true;
Planet next = null;
int minCost = Integer.MAX_VALUE;
for (Planet planet : planets) {
if (visited[planet.index]) continue;
if (minCost > current.getCost(planet)){
minCost = current.getCost(planet);
next = planet;
}
}
if (next != null) {
queue.add(next);
answer += minCost;
}
}
System.out.println(answer);
}
static class Planet {
int index;
int x;
int y;
int z;
public Planet(int index, int x, int y, int z) {
this.index = index;
this.x = x;
this.y = y;
this.z = z;
}
public int getCost(Planet other) {
return Math.min(Math.min(Math.abs(this.x - other.x), Math.abs(this.y - other.y)), Math.abs(this.z - other.z));
}
}
}
결과
혹시나 했지만 100,000^2은 역시나 시간초과가 발생한다. 그러면 단순 크루스칼, 프림 알고리즘으로 해결하는 것이 아니라고 생각이 들었다.
해결 방법 - 2
아이디어
- 해결 방법 -2 는 steady-coding.tistory.com/117 를 보고 알았다.
- 제 아이디어에서 문제점은 모든 행성들 사이를 간선으로 만들어질 수 있다는 점이 문제라고 생각이 든다.
- 모든 행성을 x축만 있다고 생각하고 x축으로 정렬했을 시 예로 0, 3, 5, 8, 10으로 정렬되었다면 내가 고려해야 하는 간선은 0-3, 3-5, 5-8, 8-10만 고려하면 되었다는 것이다.
- 마찬가지로 y축, z축도 동일하게 간선을 만들게되면 대략 300,000 간선이 나오고 이 간선을 가지고 크루스칼 알고리즘을 수행해보자.
실제 풀이 코드
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int planetCount = Integer.parseInt(br.readLine());
List<Planet> planets = new ArrayList<>();
for (int i = 0; i < planetCount; i++) {
String[] split = br.readLine().split(" ");
planets.add(new Planet(i, Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2])));
}
List<Tunnel> tunnels = new ArrayList<>();
planets.sort(Comparator.comparingInt(p -> p.x));
for (int i = 0; i < planets.size() - 1; i++) {
Planet from = planets.get(i);
Planet to = planets.get(i + 1);
tunnels.add(new Tunnel(from.index, to.index, Math.abs(from.x - to.x)));
}
planets.sort(Comparator.comparingInt(p -> p.y));
for (int i = 0; i < planets.size() - 1; i++) {
Planet from = planets.get(i);
Planet to = planets.get(i + 1);
tunnels.add(new Tunnel(from.index, to.index, Math.abs(from.y - to.y)));
}
planets.sort(Comparator.comparingInt(p -> p.z));
for (int i = 0; i < planets.size() - 1; i++) {
Planet from = planets.get(i);
Planet to = planets.get(i + 1);
tunnels.add(new Tunnel(from.index, to.index, Math.abs(from.z - to.z)));
}
Collections.sort(tunnels);
int[] cycleTable = new int[planetCount];
for (int i = 0; i < planetCount; i++) {
cycleTable[i] = i;
}
int answer = 0;
for (Tunnel tunnel : tunnels) {
int parentFrom = getParent(cycleTable, tunnel.from);
int parentTo = getParent(cycleTable, tunnel.to);
if (parentFrom != parentTo) {
answer += tunnel.cost;
union(cycleTable, tunnel.from, tunnel.to);
}
}
System.out.println(answer);
}
private static void union(int[] cycleTable, int from, int to) {
int parentFrom = getParent(cycleTable, from);
int parentTo = getParent(cycleTable, to);
if (parentFrom > parentTo) cycleTable[parentFrom] = parentTo;
else cycleTable[parentTo] = parentFrom;
}
private static int getParent(int[] cycleTable, int child) {
if (cycleTable[child] == child) return child;
return getParent(cycleTable, cycleTable[child]);
}
static class Planet {
int index;
int x;
int y;
int z;
public Planet(int index, int x, int y, int z) {
this.index = index;
this.x = x;
this.y = y;
this.z = z;
}
}
static class Tunnel implements Comparable<Tunnel> {
int from;
int to;
int cost;
public Tunnel(int from, int to, int cost) {
this.from = from;
this.to = to;
this.cost = cost;
}
@Override
public int compareTo(Tunnel o) {
return Integer.compare(this.cost, o.cost);
}
}
}
결과
'알고리즘 문제 > 백준' 카테고리의 다른 글
백준 10775 공항 java (0) | 2021.02.01 |
---|---|
백준 9466 텀 프로젝트 java (0) | 2021.01.27 |
백준 9252 LCS 2 java (0) | 2021.01.25 |
백준 7453 합이 0인 네자리 수 java (0) | 2021.01.23 |
백준 4386 별자리 만들기 java (0) | 2021.01.22 |