[ALGORITHM]PROGRAMMERS/Greedy-섬연결하기
문제
n개의 섬 사이에 다리를 건설하는 비용(costs)이 주어질 때, 최소의 비용으로 모든 섬이 서로 통행 가능하도록 만들 때 필요한 최소 비용을 return 하도록 solution을 완성하세요.
다리를 여러 번 건너더라도, 도달할 수만 있으면 통행 가능하다고 봅니다. 예를 들어 A 섬과 B 섬 사이에 다리가 있고, B 섬과 C 섬 사이에 다리가 있으면 A 섬과 C 섬은 서로 통행 가능합니다.
제한사항
- 섬의 개수 n은 1 이상 100 이하입니다.
- costs의 길이는
((n-1) * n) / 2
이하입니다. - 임의의 i에 대해, costs[i][0] 와 costs[i] [1]에는 다리가 연결되는 두 섬의 번호가 들어있고, costs[i] [2]에는 이 두 섬을 연결하는 다리를 건설할 때 드는 비용입니다.
- 같은 연결은 두 번 주어지지 않습니다. 또한 순서가 바뀌더라도 같은 연결로 봅니다. 즉 0과 1 사이를 연결하는 비용이 주어졌을 때, 1과 0의 비용이 주어지지 않습니다.
- 모든 섬 사이의 다리 건설 비용이 주어지지 않습니다. 이 경우, 두 섬 사이의 건설이 불가능한 것으로 봅니다.
- 연결할 수 없는 섬은 주어지지 않습니다.
입출력 예
n | costs | return |
---|---|---|
4 | [[0,1,1],[0,2,2],[1,2,5],[1,3,1],[2,3,8]] | 4 |
입출력 예 설명
costs를 그림으로 표현하면 다음과 같으며, 이때 초록색 경로로 연결하는 것이 가장 적은 비용으로 모두를 통행할 수 있도록 만드는 방법입니다.
코드
import java.util.Arrays;
class Solution {
public static int[] parent;
public static int[] level;
public static int solution(int n, int[][] costs) {
int answer = 0;
parent = new int[n];
level = new int[n];
for (int i = 0; i < n; i++) parent[i] = i;
Arrays.sort(costs, ((o1, o2) -> Integer.compare(o1[2],o2[2])));
for (int i = 0; i < costs.length; i++){
int a = costs[i][0];
int b = costs[i][1];
if (findParent(a) != findParent(b)){
answer += costs[i][2];
Merge(a, b);
}
}
return answer;
}
public static void Merge(int a, int b){
a = findParent(a);
b = findParent(b);
if (a == b) return;
if (level[a] < level[b]){
int tmp = a;
a = b;
b = tmp;
}
parent[b] = a;
if (level[a] == level[b]) level[a]++;
}
public static int findParent(int a){
if (parent[a] == a) return a;
return parent[a] = findParent(parent[a]);
}
}
-
디스조인트-셋 문제와 아주 비슷하다.
-
처음에는 정렬을 해준 후 visited만 체크를 해주었는데, 1-2 3-4인 경우 2-3인 경우를 계산해주지 않고 끝나는 경우가 있었다. 그래서 트리 형식으로 생각을 하였다.
-
우선 cost값으로 정렬을 해주었다.
Arrays.sort(costs, ((o1, o2) -> Integer.compare(o1[2],o2[2])));
-
그 다음 간선 두개가 이미 연결되어 있지 않은 경우, cost를 더해주고, 연결을 했다.
if (findParent(a) != findParent(b)){ answer += costs[i][2]; Merge(a, b); }
-
두 점간의 연결은
public static void Merge(int a, int b){ //가장 상위 노드끼리 merge해줘야 하므로 findParent를 하였다. a = findParent(a); b = findParent(b); if (a == b) return; // 이미 연결되어 있는 경우를 뜻한다. 아마 여기는 들어오는 것은 없을 것이다.if (findParent(a) != findParent(b))이 조건문 때문 if (level[a] < level[b]){ int tmp = a; a = b; b = tmp; } parent[b] = a; if (level[a] == level[b]) level[a]++; }
-
람다식 sort 기억하자!
Arrays.sort(costs, new Comparator<int[]>() { @Override public int compare(int[] o1, int[] o2) { return Integer.compare(o1[2], o2[2]); } }); //👇 Arrays.sort(costs, ((o1, o2) -> Integer.compare(o1[2],o2[2])));
느낀점
어느새 순열늪에 빠져서 자꾸 순열부터 짠다. 그리고 시간초과가 와장창 나서 25점 받고,,생각하자 생각하자,,,해서,,,,일단 정렬해보고 그다음 Tree형태를 떠올려 저번에 푼 디스조인트셋처럼 풀었다.. 생각좀 하고 풀자!!!
문제 : https://programmers.co.kr/learn/courses/30/lessons/42861
저장소 : https://github.com/yoo-chaewon/HELLO_JAVA/blob/master/Algorithm/2.PROGRAMMERS/with%20Study/Greedy_섬연결하기.java