[백준 1774] 우주신과의 교감 (JAVA)
백준(BOJ) 1774 우주신과의 교감
난이도
221227 기준 골드 3
사용 알고리즘
최소스패닝트리(MST) - 프림
문제 링크
https://www.acmicpc.net/problem/1774
1774번: 우주신과의 교감
(1,1) (3,1) (2,3) (4,3) 이렇게 우주신들과 황선자씨의 좌표가 주어졌고 1번하고 4번이 연결되어 있다. 그렇다면 1번하고 2번을 잇는 통로를 만들고 3번하고 4번을 잇는 통로를 만들면 신들과 선자씨끼
www.acmicpc.net
풀이 과정
1. 문제 읽기(중요한 부분 굵은 표시)
황선자씨는 우주신과 교감을 할수 있는 채널러 이다. 하지만 우주신은 하나만 있는 것이 아니기때문에 황선자 씨는 매번 여럿의 우주신과 교감하느라 힘이 든다. 이러던 와중에 새로운 우주신들이 황선자씨를 이용하게 되었다.
하지만 위대한 우주신들은 바로 황선자씨와 연결될 필요가 없다. 이미 황선자씨와 혹은 이미 우주신끼리 교감할 수 있는 우주신들이 있기 때문에 새로운 우주신들은 그 우주신들을 거쳐서 황선자 씨와 교감을 할 수 있다.
우주신들과의 교감은 우주신들과 황선자씨 혹은 우주신들 끼리 이어진 정신적인 통로를 통해 이루어 진다. 하지만 우주신들과 교감하는 것은 힘든 일이기 때문에 황선자씨는 이런 통로들이 긴 것을 좋아하지 않는다. 왜냐하면 통로들이 길 수록 더 힘이 들기 때문이다.
또한 우리들은 3차원 좌표계로 나타낼 수 있는 세상에 살고 있지만 우주신들과 황선자씨는 2차원 좌표계로 나타낼 수 있는 세상에 살고 있다. 통로들의 길이는 2차원 좌표계상의 거리와 같다.
이미 황선자씨와 연결된, 혹은 우주신들과 연결된 통로들이 존재한다. 우리는 황선자 씨를 도와 아직 연결이 되지 않은 우주신들을 연결해 드려야 한다. 새로 만들어야 할 정신적인 통로의 길이들이 합이 최소가 되게 통로를 만들어 “빵상”을 외칠수 있게 도와주자.
입력
첫째 줄에 우주신들의 개수(N<=1,000) 이미 연결된 신들과의 통로의 개수(M<=1,000)가 주어진다.
두 번째 줄부터 N개의 줄에는 황선자를 포함하여 우주신들의 좌표가 (0<= X<=1,000,000), (0<=Y<=1,000,000)가 주어진다. 그 밑으로 M개의 줄에는 이미 연결된 통로가 주어진다. 번호는 위의 입력받은 좌표들의 순서라고 생각하면 된다. 좌표는 정수이다.
출력
첫째 줄에 만들어야 할 최소의 통로 길이를 출력하라. 출력은 소수점 둘째짜리까지 출력하여라.
2. 어떤 알고리즘을 사용할 것인가
황선자씨와 모든 우주신이 연결되는 최소의 통로 길이를 출력하는 문제입니다. 최소 스패닝 트리(MST) 문제네요. 프림과 크루스칼 둘다 사용할 수는 있지만 모든 정점간의 거리를 계산 해서 간선을 어차피 전부 구해야 하기 때문에 프림을 쓰는게 조금 더 효율적으로 보입니다.
3. 풀이 구상
일단 황선자씨와 우주신들의 좌표를 가지고 각 정점간의 거리를 간선의 길이로하는 인접배열을 만들어줍니다. 이미 연결된 통로는 visited 처리 해주고 visitedCnt도 미리 늘려준 뒤 프림 알고리즘을 쓰면 자연스럽게 문제가 풀릴 것 같습니다.
간선의 길이는 점과 점사이의 거리 공식으로 구할텐데 공식 안에 제곱을 하는 부분이 있기에 int로 계산하면 터질 가능성이 있습니다. 따라서 long을 사용합니다.
여기까지만 구상하고 문제를 풀어보겠습니다.
4. 코드 작성
1. 정답 코드
import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
// N 우주신들의 개수, M 이미 연결된 신들과의 통로의 개수
static int N, M;
static long[][] xyarr;
static boolean visited[];
static int visitedCnt;
static double[][] adjMatrix;
static double distSum;
static double dist[];
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());
// 0번 인덱스 버림
xyarr = new long[N + 1][2];
visited = new boolean[N + 1];
visitedCnt = 0;
distSum = 0;
adjMatrix = new double[N + 1][N + 1];
dist = new double[N + 1];
Arrays.fill(dist, Double.MAX_VALUE);
for (int i = 1; i <= N; i++) {
st = new StringTokenizer(br.readLine());
xyarr[i][0] = Long.parseLong(st.nextToken());
xyarr[i][1] = Long.parseLong(st.nextToken());
}
for (int i = 1; i <= N; i++) {
for (int j = i + 1; j <= N; j++) {
if (i == j) {
continue;
}
long d0 = (xyarr[i][0] - xyarr[j][0]) * (xyarr[i][0] - xyarr[j][0]);
long d1 = (xyarr[i][1] - xyarr[j][1]) * (xyarr[i][1] - xyarr[j][1]);
long ds = d0 + d1;
adjMatrix[i][j] = adjMatrix[j][i] = Math.sqrt(ds);
}
}
for (int i = 1; i <= M; i++) {
st = new StringTokenizer(br.readLine());
int left = Integer.parseInt(st.nextToken());
int right = Integer.parseInt(st.nextToken());
adjMatrix[left][right] = 0;
adjMatrix[right][left] = 0;
}
if (N != 0) {
prim();
}
System.out.printf("%.2f", (double) Math.round(distSum * 100) / 100);
br.close();
}
static void prim() {
int minidx = -1;
double mindist = Double.MAX_VALUE;
dist[1] = 0;
for (int j = 2; j <= N; j++) {
dist[j] = Math.min(dist[j], adjMatrix[1][j]);
if (mindist > dist[j]) {
mindist = dist[j];
minidx = j;
}
}
visited[1] = true;
visitedCnt++;
if (visited[minidx] == false) {
distSum += mindist;
visited[minidx] = true;
visitedCnt++;
}
while (visitedCnt < N) {
visited[minidx] = true;
int nextidx = minidx;
mindist = Double.MAX_VALUE;
for (int i = 1; i <= N; i++) {
if (i == nextidx) {
continue;
}
if (visited[i]) {
continue;
}
dist[i] = Math.min(dist[i], adjMatrix[nextidx][i]);
if (mindist > dist[i]) {
mindist = dist[i];
minidx = i;
}
}
distSum += mindist;
visitedCnt++;
}
}
}
2. 오답노트
역대급 오답 잔치였습니다. 풀이 설계자체를 잘못해 놓고 잘못된 설계대로 고치다보니 계속해서 산으로 갔네요.
1. 잘못된 설계
처음에는 이미 연결된 행성들의 프림 메소드에 들어가는 dist 부분을 0으로 만들면 되지 않나? 라는 생각을 했으나 이부분에서 완전히 틀려버렸습니다.
아무튼 처음 설계는 코드가 복잡하기까지 해서 뭔가 잘못된 게 분명하다 싶어 처음부터 되짚어 봤습니다. dist 부분을 바로 0을 만들면 연결되지 않은 행성들과 연결 되어 있는 행성과의 최단거리를 찾을 수 없다는걸 발견하고 adjMatrix에서 연결된 행성간의 거리만 0으로 만들어주고 문제를 해결했습니다.
adjMatrix에 연결된 행성간의 거리만 0으로 만들어주는걸 처음에 생각은 했었는데 괜히 멋있게 풀어보려다 대 실패!
풀이 후기
안그래도 할 게 많은데 쉽게 봤던 문제가 시간을 왕창 잡아먹었네요 이번주 남은 날들은 실버정도로 널널하게 풀어보는걸로..