🔗4386번 : 별자리 만들기
● Java 풀이
● 시간 제한 1초
● 메모리 제한 128 MB
✅ 문제
도현이는 우주의 신이다. 이제 도현이는 아무렇게나 널브러져 있는 n개의 별들을 이어서 별자리를 하나 만들 것이다. 별자리의 조건은 다음과 같다.
- 별자리를 이루는 선은 서로 다른 두 별을 일직선으로 이은 형태이다.
- 모든 별들은 별자리 위의 선을 통해 서로 직/간접적으로 이어져 있어야 한다.
별들이 2차원 평면 위에 놓여 있다. 선을 하나 이을 때마다 두 별 사이의 거리만큼의 비용이 든다고 할 때, 별자리를 만드는 최소 비용을 구하시오.
✅ 테스트 케이스
/** 출처 (https://www.acmicpc.net/board/view/82327) **/
// input
6
4 1
5 8
2 1
8 4
2 9
1 4
// answer
18.32
✅ 풀이
import java.io.*;
import java.util.*;
public class Main {
private static class Dist {
public Dist(int index1, int index2, double dist) {
this.index1 = index1;
this.index2 = index2;
this.dist = dist;
}
int index1, index2; // 두 노드
double dist; // 두 노드를 잇는 간선의 가중치
}
private static int N;
private static int[] roots; // 노드의 루트노드 삽입할 배열
private static List<Dist> distList; // 간선 리스트
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
N = Integer.parseInt(br.readLine());
double[][] stars = new double[N][2]; // 각 별의 위치 담을 2차원 배열
roots = new int[N];
Arrays.fill(roots, -1); // 처음에는 루트 노드가 없다고 정의
distList = new ArrayList<>();
StringTokenizer st = new StringTokenizer(br.readLine());
stars[0][0] = Double.parseDouble(st.nextToken());
stars[0][1] = Double.parseDouble(st.nextToken());
for (int i = 1; i < N; i++) {
st = new StringTokenizer(br.readLine());
stars[i][0] = Double.parseDouble(st.nextToken());
stars[i][1] = Double.parseDouble(st.nextToken());
for (int j = 0; j < i; j++) { // 각 간선의 가중치 담기 (무방향)
distList.add(new Dist(j, i, Math.sqrt(Math.pow(stars[j][0] - stars[i][0], 2) + Math.pow(stars[j][1] - stars[i][1], 2))));
}
}
distList.sort(Comparator.comparingDouble(o -> o.dist)); // 간선의 가중치가 작은 순으로 정렬
System.out.println(findShortestLine()); // 최소 신장 출력
}
private static double findShortestLine() {
double sum = 0; // 가중치의 합을 담을 변수 선언
int maxRoot, minRoot; // 루트 노드를 변경하기 위한 변수 선언
for (Dist dist : distList) {
if (roots[dist.index1] == -1 && roots[dist.index2] == -1) { // 두 노드 모두 루트가 없을 경우
roots[dist.index2] = roots[dist.index1] = Math.min(dist.index1, dist.index2); // 인덱스가 더 작은 노드를 루트로 설정
} else if (roots[dist.index1] == roots[dist.index2]) { // 사이클이 생성될 경우
continue; // 연결하지 않음 (가중치 합 X)
} else if (roots[dist.index1] == -1 || roots[dist.index2] == -1) { // 둘 중 하나의 노드가 루트가 없을 경우
roots[dist.index1] = roots[dist.index2] = Math.max(roots[dist.index1], roots[dist.index2]); // 루트 공유
} else {
minRoot = Math.min(roots[dist.index1], roots[dist.index2]); // 인덱스가 더 작은 루트 노드를 최종 루트로 설정
maxRoot = Math.max(roots[dist.index1], roots[dist.index2]); // 인덱스가 더 큰 루트 확인
for (int i = 0; i < N; i++) {
roots[i] = (roots[i] == maxRoot) ? minRoot : roots[i]; // 인덱스가 더 큰 루트를 최종 루트의 자식으로 변경
}
}
sum += dist.dist; // 해당 간선 가중치 합하기
}
return Math.round(sum * 100.0) / 100.0; // 소수점 2자리 수에서 반올림
}
}
크루스칼 알고리즘을 활용해 최소 신장 트리를 구하는 문제.
크루스칼 알고리즘을 알면 쉽게 풀 수 있는 문제이다.
최근 다양한 알고리즘을 접하며 느낀 것은, 내가 알고 있는 알고리즘 종류가 매우 제한적이라는 것.
블로그에 문제 풀이만이 아니라 알고리즘 별 개념을 올리며 복기하는 시간을 가지면 좋을 것 같다.
'알고리즘 및 데이터 구조 > 백준' 카테고리의 다른 글
[백준] 14719번 : 빗물 (Java) (0) | 2024.03.24 |
---|---|
[백준] 11000번 : 강의실 배정 (Java) (0) | 2024.03.24 |
[백준] 17135번 : 캐슬 디펜스 (Java) (2) | 2024.03.18 |
[백준] 1516번 : 게임 개발 (Java) (0) | 2024.03.16 |
[백준] 16235번 : 나무 재테크 (Java) (0) | 2024.03.14 |