1. 신장 트리(Spanning Tree)
- 그래프의 모든 정점을 최소한 적은 간선만 사용해 연결하는 트리를 말한다
- 정점이 N개라면 N-1개의 간선만 사용해 모든 정점을 연결한 형태를 이루게 된다
- 신장 트리는 그 구조상 사이클을 포함하지 않는다
- 만드는 방법 : 한 정점을 기준으로 DFS/BFS를 실행할 때, 탐색한 간선들이 곧 하나의 신장 트리가 된다
2. 최소 신장 트리(Minimum Spanning Tree, MST)
- 최소 신장 트리란, 가중치 있는 그래프에서, 신장트리를 이루는 간선의 가중치 총합이 최소가 되는 신장트리를 말한다
- 모든 정점들을 최소 비용으로 연결하는 것
3. 최소 신장 트리 구현
- 크루스칼(KRUSKAL) 알고리즘 (간선 기반)
- Greedy하게 간선을 선택하여 최소 신장 트리를 완성한다
- 간선의 가중치가 작을수록 선택에 우선순위를 두되,
- 해당 간선을 선택함으로 인해 트리에 사이클이 발생한다면, 선택하지 않고 넘어간다
- n-1개의 간선을 선택하면, MST가 완성된 것이다
- 사이클의 발생 여부 탐색 : 간선의 양쪽 정점 사이에 이미 경로가 존재한다면, 사이클이 발생하게 된다
- 분리집합을 이용해 간선 선택시 양 정점의 집합을 합쳐, 사이클 발생을 막을 수 있다
- 프림(PRIM) 알고리즘 (정점 기반)
- 한 정점에서 시작해 최적의 간선만을 선택해 트리를 확장해 나간다
- 현재 트리에 속한 정점들의 인접 정점 중, 가장 적은 비용으로 진행할 수 있는 정점을 선택, 트리에 포함시킨다
- n-1개의 정점을 선택하면, MST가 완성된 것이다
- 다익스트라와 형태가 거의 비슷하지만, 다익스트라는 특정 노드를 기준으로 누적된 거리를 기준으로 한다
4. JAVA 기초 코드(문제링크)
- Kruskal 코드
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main{
public static void main(String[] args)throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
int n = pint(st.nextToken());
int m = pint(st.nextToken());
//간선 리스트..대신 크기가 주어지니 간선배열 사용
edge[] e = new edge[m];
for (int i = 0; i < m; i++) {
e[i]=new edge();
st = new StringTokenizer(br.readLine(), " ");
e[i].n1=pint(st.nextToken());
e[i].n2=pint(st.nextToken());
e[i].cost=pint(st.nextToken());
}
//정렬, 작은 간선부터 확인
Arrays.sort(e);
//분리집합 초기화
init(n);
int cnt=0, i=0;//현재 선택한 edge의 갯수, 현재 edge index
int totalCost=0;//전체cost
while(true) {
//union이 정상적으로 수행 됬다면 ( = cycle을 이루지 않는 간선이 tree에 포함되었다면)
if(union(e[i].n1, e[i].n2)) {
//cnt, cost 반영
cnt++;
totalCost+=e[i].cost;
}
if(cnt==n-1)break;//n-1개의 간선을 선택했다면 MST의 완성
i++;
}
//최소 cost의 2개의 마을로 분할하는 법 = MST에서 가장 cost가 큰 edge 하나 자르기
System.out.println(totalCost-e[i].cost);
}
//분리집합
static int[]parents;
static void init(int n) {
parents=new int[n+1];
for (int i = 0; i <= n; i++) {
parents[i]=i;
}
}
static int find(int n) {
if(parents[n]==n)return n;
return parents[n]=find(parents[n]);
}
static boolean union(int n1, int n2) {
int p1=find(n1);
int p2=find(n2);
if(p1==p2)return false;
parents[p1]=p2;
return true;
}
static int pint(String s) {
return Integer.parseInt(s);
}
//간선 객체
static class edge implements Comparable<edge>{
int n1;
int n2;
int cost;
@Override
public int compareTo(edge e) {
return this.cost-e.cost;
}
}
}
- Prim 코드
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
public class Main{
public static void main(String[] args)throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
int n = pint(st.nextToken());
int m = pint(st.nextToken());
ArrayList<ArrayList<int[]>>graph = new ArrayList<>();
for (int i = 0; i <= n; i++) {
graph.add(new ArrayList<>());
}
//그래프 입력
for (int i = 0; i < m; i++) {
st = new StringTokenizer(br.readLine(), " ");
int x = pint(st.nextToken());
int y = pint(st.nextToken());
int cost = pint(st.nextToken());
graph.get(x).add(new int[] {y,cost});
graph.get(y).add(new int[] {x,cost});
}
int totalCost=0, cnt=0, max=-1;
//전처리
boolean[] isV = new boolean[n+1];
PriorityQueue<int[]>pq = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
// TODO Auto-generated method stub
return o1[1]-o2[1];
}
});
for (int i = 1; i <= n; i++) {
pq.offer(new int[] {i,Integer.MAX_VALUE});
}
//임의의 시작 노드 설정
pq.offer(new int[] {1,0});
while(cnt!=n) {//n개의 정점을 선택하면 MST의 완성
int[] curNode = pq.poll();
if(isV[curNode[0]])continue;//이미 방문한 node면 무시한다
isV[curNode[0]]=true;
totalCost+=curNode[1];
if(max<curNode[1])max=curNode[1];//MST의 가장 큰 edge 저장
cnt++;
for (int i = 0; i < graph.get(curNode[0]).size(); i++) {
int[] next = graph.get(curNode[0]).get(i);
if(!isV[next[0]]) {
pq.offer(next);
}
}
}
System.out.println(totalCost-max);
}
static int pint(String s) {
return Integer.parseInt(s);
}
}
'알고리즘 공부' 카테고리의 다른 글
그래프(Graph)에 대해 (0) | 2021.05.30 |
---|---|
너비 우선 탐색(BFS), 깊이 우선 탐색(DFS)에 대해 (0) | 2021.05.30 |