코딩 테스트
[백준] 우주신과의 교감 1774번 자바
한 면만 쓴 종이
2025. 3. 19. 13:37
https://www.acmicpc.net/problem/1774
기본적인 MST 문제입니다.
최소신장트리
그래프의 모든 정점을 사이클 없이 잇는 트리에서 간선의 가중치의 합이 최소가 되는 트리
크루스칼 알고리즘
간선을 하나씩 늘려가면서 이어줍니다.
[풀이한 코드]
package mst;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
public class B1774 {
static class Node {
int x;
int y;
Node(int x, int y) {
this.x = x;
this.y = y;
}
}
static class Edge implements Comparable<Edge> {
int a;
int b;
double dist;
public Edge(int a, int b, double dist) {
this.a = a;
this.b = b;
this.dist = dist;
}
@Override
public int compareTo(Edge o) {
return Double.compare(this.dist, o.dist);
}
}
static int[] parents;
static Node[] nodes;
static int N;
static int M;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
nodes = new Node[N + 1];
parents = new int[N + 1];
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());
nodes[i + 1] = new Node(x, y);
parents[i + 1] = i + 1;
}
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
unionParent(a, b);
}
PriorityQueue<Edge> pq = new PriorityQueue<>();
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
double cost = Math.sqrt(Math.pow(nodes[i].x - nodes[j].x, 2) + Math.pow(nodes[i].y - nodes[j].y, 2));
pq.add(new Edge(i, j, cost));
}
}
double cost = 0;
while (!pq.isEmpty()) {
Edge edge = pq.poll();
if (findParent(edge.a) != findParent(edge.b)) {
unionParent(edge.a, edge.b);
cost += edge.dist;
}
}
System.out.printf("%.2f", cost);
}
static void unionParent(int a, int b) {
a = findParent(a);
b = findParent(b);
if (a < b) {
parents[b] = a;
} else {
parents[a] = b;
}
}
static int findParent(int x) {
if (parents[x] == x) {
return x;
}
return parents[x] = findParent(parents[x]);
}
}
[시간복잡도]
1. 이미 연결된 간선 처리
- M개의 이미 연결된 간선을 처리합니다.
- 이때, Union Find 알고리즘을 사요했습니다. 해당 알고리즘은 O(1)에 가까운 시간복잡도를 갖습니다.
2. 모든 노드 간 거리를 우선순위 큐에 삽입
- 우선순위 큐에 삽입하는 연산: O(logN)
-> 이중 반복문을 돌리며 우선순위 큐에 삽입하는 연산: O(N^2 logN)
3. 크루스칼 알고리즘
- 우선순위 큐에서 poll() 연산: O(logN)
- unionParent() 연산: O(1) 에 가까움
-> N^2 번 수행
-> O(N^2 logN)
따라서 최종 시간복잡도는 O(N^2 logN) 입니다.
[기억하자]
1. inner class 의 비교 방법
- Comparable<Edge> 를 implements하기
- @Override public int compareTo() 구현
static class Edge implements Comparable<Edge> {
int a;
int b;
double dist;
public Edge(int a, int b, double dist) {
this.a = a;
this.b = b;
this.dist = dist;
}
@Override
public int compareTo(Edge o) {
return Double.compare(this.dist, o.dist);
}
}
2. 제곱할 때 x * x는 오버플로 발생 가능성이 있기 때문에 Math.pow() 사용하기
- 단, 반환값은 항상 double이다.
- Math.pow(x, 2) 를 하면 x의 2제곱
3. 출력할 때 소수점 2번째 자리까지 반올림하기
- System.out.printf("%.2f", 수);