https://www.acmicpc.net/problem/1197
문제
그래프가 주어졌을 때, 그 그래프의 최소 스패닝 트리를 구하는 프로그램을 작성하시오.
최소 스패닝 트리는, 주어진 그래프의 모든 정점들을 연결하는 부분 그래프 중에서 그 가중치의 합이 최소인 트리를 말한다.
입력
첫째 줄에 정점의 개수 V(1 ≤ V ≤ 10,000)와 간선의 개수 E(1 ≤ E ≤ 100,000)가 주어진다. 다음 E개의 줄에는 각 간선에 대한 정보를 나타내는 세 정수 A, B, C가 주어진다. 이는 A번 정점과 B번 정점이 가중치 C인 간선으로 연결되어 있다는 의미이다. C는 음수일 수도 있으며, 절댓값이 1,000,000을 넘지 않는다.
그래프의 정점은 1번부터 V번까지 번호가 매겨져 있고, 임의의 두 정점 사이에 경로가 있다. 최소 스패닝 트리의 가중치가 -2,147,483,648보다 크거나 같고, 2,147,483,647보다 작거나 같은 데이터만 입력으로 주어진다.
출력
첫째 줄에 최소 스패닝 트리의 가중치를 출력한다.
크루스칼 알고리즘
을 이용하여 풀이했다.
또한 프림 알고리즘
을 이용해도 정답을 얻을 수 있다.
Minimum Spanning Tree
무방향 그래프
G(V,E)에서 E에 속한 간선들최소 비용 신장 트리(최소 신장 트리)
Greedy
하게 모든 정점을 최소 비용으로 연결
- 모든 간선을 가중치 기준으로 오름차순으로 정렬
- 간선들을 순서대로 모든 정점이 연결될 때까지 연결
Union-Find 알고리즘
이용하여 정점 연결(union)
O(ElogE)
(퀵정렬 사용시)정점 선택 기반
으로 작동
- 트리 집합을 단계적으로 확장
- 새로운 정점을 연결할 때마다 새로운 정점에서 갈 수 있는 아직 연결되지 않은 정점들에 대한 간선을 추가
Priority Queue
를 이용한 최소힙으로 구현 가능
O(ElogV)
package BOJ.graph;
import java.util.*;
import java.io.*;
public class No1197_최소스패닝트리_크루스칼 {
static int[] parent;
public static void main(String[] args) throws IOException{
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st=new StringTokenizer(br.readLine());
int V=Integer.parseInt(st.nextToken());
int E=Integer.parseInt(st.nextToken());
ArrayList<Edge> edges=new ArrayList<>();
int answer=0;
for(int i=0;i<E;i++){
st=new StringTokenizer(br.readLine());
int start=Integer.parseInt(st.nextToken());
int end=Integer.parseInt(st.nextToken());
int cost=Integer.parseInt(st.nextToken());
edges.add(new Edge(start, end, cost));
}
//cost 기준으로 정렬(or 우선순위큐 써줘도 됨)
edges.sort(new Comparator<Edge>(){
@Override
public int compare(Edge o1, Edge o2) {
return Integer.compare(o1.c, o2.c);
}
});
//parent 초기화
//처음에는 자기자신을 root로 가지도록 설정
parent=new int[V+1];
for(int i=1;i<V+1;i++){
parent[i]=i;
}
//트리 계산
//isSameParent==true -> cycle 발생
//false인 경우 cost를 answer에 합산 한 후, 한 트리로 union
for(int i=0;i<E;i++){
Edge edge=edges.get(i);
if(!isSameParent(edge.s, edge.e)){
answer+=edge.c;
union(edge.s, edge.e);
}
}
System.out.println(answer);
}
//parent 찾기
static int findParent(int x){
//루트가 아니면 재귀로 parent 찾기
if(parent[x]!=x){
parent[x]=findParent(parent[x]);
}
return parent[x];
}
//같은 parent인지 판별
static boolean isSameParent(int x, int y){
x=findParent(x);
y=findParent(y);
if(x==y)return true;
else return false;
}
//Union
static void union(int x, int y){
x=findParent(x);
y=findParent(y);
if(x<y){
parent[y]=x;
}
else{
parent[x]=y;
}
}
}
class Edge{
int s, e, c;
Edge(int s, int e, int c){
this.s=s;
this.e=e;
this.c=c;
}
}