[백준] 1197번 - 최소 스패닝 트리

야금야금 공부·2024년 9월 8일
0

백준

목록 보기
48/52

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보다 작거나 같은 데이터만 입력으로 주어진다.

출력

첫째 줄에 최소 스패닝 트리의 가중치를 출력한다.


제출 코드

  • 크루스칼 알고리즘을 이용해 최소 스패닝 트리 구하기
  • 최소 스패닝 트리 : 모든 정점을 최소한의 간선 비용으로 연결하는 트리
  • 주의 할 점 : 더 작은 값의 정점을 부모로 설정해주어야 코드가 통과한다.

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Solution {

	private static int v, e;
	private static PriorityQueue<Edge> pre;
	static long min;  // 최소 스패닝 트리의 가중치 합을 저장

	static class Edge implements Comparable<Edge> {

		int s, e;
		int weight;

		public Edge(int s, int e, int weight) {
			this.e = e;
			this.s = s;
			this.weight = weight;
		}

		@Override
		public int compareTo(Edge o) {  // 오름차순으로 정렬

			return Integer.compare(weight, o.weight);
		}

	}

	static int[] parents;  // 부모 노드 저장할 배열(union-find)

	public static void main(String[] args) throws Exception {

		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		String[] split = in.readLine().split(" ");
		v = Integer.parseInt(split[0]);
		e = Integer.parseInt(split[1]);

		parents = new int[v + 1];
		pre = new PriorityQueue<>();

		StringTokenizer st;
		for (int i = 0; i < e; i++) {
			st = new StringTokenizer(in.readLine());
			int s = Integer.parseInt(st.nextToken());
			int e = Integer.parseInt(st.nextToken());
			int w = Integer.parseInt(st.nextToken());
            
            // from과 to를 가중치 w의 간선으로 연결
			pre.offer(new Edge(s, e, w));
		}

		// 각 정점을 초기화(자기 자신이 부모인 집합)
		makeSet();

		int cnt = 0;  // 선택된 간선의 수

		// 우선순위 큐에서 하나씩 꺼내어 처리
		while (!pre.isEmpty()) {

			Edge edge = pre.poll();  // 가중치가 가장 작은 간선을 꺼냄
			if (union(edge.e, edge.s)) {   // 연결할 수 있으면 true
				min += edge.weight;  // MST에 간선의 가중치 더하기
				cnt++;  // 선택한 간선 수 증가
				
                // 최소 스패닝 트리의 간선 수가 (정점 수 - 1) 이 되면 종료
				if (cnt == v - 1)
					break;
			}
		}
		
        // MST의 가중치 합 출력
		System.out.println(min);

	}

	// 두 정점을 연결하는 union 메서드
	private static boolean union(int from, int to) {

		from = find(from);  // from 정점의 부모 찾기
		to = find(to);      // to 정점의 부모 찾기
		
        // 두 정점이 같은 집합이면, 사이클이 생성되므로 연결x (false 반환)
		if (from == to)
			return false;
            
        // 서로 다른 집합이면, 한쪽을 부모로 설정하여 연결
        // 경로 탐색의 시간 소요를 줄이기 위해 더 작은 값의 정점을 부모로 설정
		if (parents[from] > parents[to]) {
			parents[from] = to;
		} else {
			parents[to] = from;
		}

		return true;
	}

	// 정점 v의 부모를 찾는 함수
	private static int find(int v) {
		
        // 자기자신이 부모이면 v 리턴
		if (parents[v] == v)
			return v;
		else {  // 재귀적으로 부모를 찾음
			return parents[v] = find(parents[v]);
		}

	}
	
    // 모든 정점을 초기화하는 함수
	private static void makeSet() {
		for (int i = 1; i <= v; i++) {
			parents[i] = i;
		}
	}

}

참고 내용

0개의 댓글