[SWEA] - 하나로 / 1251번 (Java)
1. 문제 접근
각 섬의 좌표가 주어지고, 각 섬을 연결하고자할 때 섬을 연결하는 최소 비용을 계산 하면 되는 문제다.
최소 비용으로 모든 섬을 연결하기 위해 MST(Kruskal) 알고리즘을 통해 구현했다.
구현의 흐름은 다음과 같다.
- 간선의 정보가 주어지지 않았으므로, 완전그래프 형태로 모든 간선을 생성하여 간선 리스트에 저장한다.
- 간선 리스트를 환경부담금(`cost`)을 기준으로 오름차순으로 정렬한다.
- 환경부담금(`cost`)이 작은 간선부터 트리에 연결한다.
- 이 때, 사이클이 생길 경우 해당 간선은 제외한다.
- 간선의 개수가 V-1(V: 섬 개수)가 된다면 종료한다.
완전 그래프에서 간선의 개수는 V(정점의 개수) * (V - 1) / 2가 된다.
추가적으로 입력 내용을 보면, "섬들의 정수인 Y좌표가 주어진다 (0≤X≤1,000,000, 0≤Y≤1,000,000)"라고 명시 되어있다.
이 때, 거리의 제곱 값의 최대값는 100만의 제곱이 되는데 이는 `int`형으로는 오버플로우가 발생한다.
따라서 가중치를 다루는 변수들은 `long`형으로 처리해야한다.
this.distSquare =
(long)Math.abs(islands[to].x - islands[from].x) * (long)Math.abs(islands[to].x - islands[from].x)+
(long)Math.abs(islands[to].y - islands[from].y) * (long)Math.abs(islands[to].y - islands[from].y);
this.cost = (double)distSquare * E;
2. 코드
package swea;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
/**
* [SWEA] - 하나로 / 1251번
* @author JinHxxxxKim
*
* 1. 전체 테스트 케이스의 수를 입력받는다.
* 2. 섬의 개수 N을 입력받는다.
* 3. 각 섬들의 좌표를 입력받는다.
* 4. 해저터널 건설의 환경 부담 세율 실수 E를 입력받는다.
* 5. 완전 그래프 형태로 모든 섬에 대한 간선을 생성하여 간선 리스트에 저장한다.
* 6. 간선 리스트를 환경부담금 기준으로 오름차순으로 정렬한다.
* 7. MST 알고리즘 실행
*
*/
public class Sol_1251 {
private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
private static StringBuilder sb = new StringBuilder();
private static StringTokenizer st;
private static int N; // 섬의 개수
private static int edgeNum; // 간선의 수
private static Island[] islands; // 섬 위치 정보 배열
private static double E; // 세율
private static Edge[] edgeList; // 간선 리스트
// unionFind 변수
private static int[] parents;
public static void main(String[] args) throws Exception {
int TC = Integer.parseInt(br.readLine().trim());
for (int testCase = 1; testCase <= TC; testCase++) {
N = Integer.parseInt(br.readLine().trim());
islands = new Island[N];
// 완전 그래프의 간선의 최대개수
edgeNum = (N * (N - 1)) / 2;
// 섬 위치 정보 저장
for (int islandCnt = 0; islandCnt < N; ++islandCnt) {
islands[islandCnt] = new Island();
}
st = new StringTokenizer(br.readLine().trim());
for (int islandCnt = 0; islandCnt < N; ++islandCnt) {
int x = Integer.parseInt(st.nextToken());
islands[islandCnt].x = x;
}
st = new StringTokenizer(br.readLine().trim());
for (int islandCnt = 0; islandCnt < N; ++islandCnt) {
int y = Integer.parseInt(st.nextToken());
islands[islandCnt].y = y;
}
// 세율 입력
E = Double.parseDouble(br.readLine().trim());
edgeList = new Edge[edgeNum];
int edgeIdx = 0;
// 완전 그래프 형태로 간선 저장 (Max: 1000 x 1000)
for (int node1 = 0; node1 < N; ++node1) {
for (int node2 = node1; node2 < N; ++node2) {
if(node1 == node2) continue;
edgeList[edgeIdx++] = new Edge(node1, node2);
}
}
makeSet();
// 가중치 기준 오름차순 정렬
Arrays.sort(edgeList);
// MST 알고리즘 실행
double totalCost = 0;
int edgeCnt = 0;
for (Edge currEdge : edgeList) {
if(union(currEdge.to, currEdge.from)) {
totalCost += currEdge.cost;
++edgeCnt;
}
if(edgeCnt == N-1)
break;
}
sb.append(String.format("#%d %.0f\n", testCase, totalCost));
}
System.out.println(sb);
}
// unionFind 전처리
private static void makeSet() {
parents = new int[N];
for (int node = 0; node < N; ++node) {
parents[node] = node;
}
}
// 매개변수로 전달된 node의 루트를 찾는 메소드
private static int find(int node) {
if (parents[node] == node)
return node;
else
return parents[node] = find(parents[node]);
}
// 매개변수로 전달된 node1, node2의 집합을 합치는 메소드
private static boolean union(int node1, int node2) {
int aRoot = find(node1);
int bRoot = find(node2);
if (aRoot == bRoot)
return false;
parents[aRoot] = bRoot;
return true;
}
// 섬의 위치 정보 저장
static class Island {
int x, y;
public Island() {
super();
}
public Island(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
// 간선 클래스
static class Edge implements Comparable<Edge> {
int to, from;
long distSquare;
double cost;
public Edge(int to, int from) {
super();
this.to = to;
this.from = from;
this.distSquare =
(long)Math.abs(islands[to].x - islands[from].x) * (long)Math.abs(islands[to].x - islands[from].x)+
(long)Math.abs(islands[to].y - islands[from].y) * (long)Math.abs(islands[to].y - islands[from].y);
this.cost = (double)distSquare * E;
}
@Override
public int compareTo(Edge edge) {
return Double.compare(this.cost, edge.cost);
}
}
}
3. 디버깅
x, y의 최대 값을 고려하지 않고 `int`형으로 가중치를 관리하고자 한다면 오버플로우가 발생하게 된다.
따라서 연산에 따른 자료형 변환에 대해 신경써서 구현해야한다.
4. 결과
'알고리즘' 카테고리의 다른 글
[SWEA] - Contact / 1238번 (Java) (0) | 2024.02.22 |
---|---|
[BOJ] - 게리맨더링 / 17471번 (Java) (0) | 2024.02.22 |
[SWEA] - 최소 스패닝 트리 / 3124번 (Java) (0) | 2024.02.22 |
[BOJ] - 암호 만들기 / 1759번 (Java) (0) | 2024.02.21 |
[BOJ] - 감시 / 15686번 (Java) (0) | 2024.02.21 |