본문 바로가기

알고리즘

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

반응형

출처 : www.acmicpc.net/problem/1197

 

1197번: 최소 스패닝 트리

첫째 줄에 정점의 개수 V(1 ≤ V ≤ 10,000)와 간선의 개수 E(1 ≤ E ≤ 100,000)가 주어진다. 다음 E개의 줄에는 각 간선에 대한 정보를 나타내는 세 정수 A, B, C가 주어진다. 이는 A번 정점과 B번 정점이 �

www.acmicpc.net

문제

그래프가 주어졌을 때, 그 그래프의 최소 스패닝 트리를 구하는 프로그램을 작성하시오.

최소 스패닝 트리는, 주어진 그래프의 모든 정점들을 연결하는 부분 그래프 중에서 그 가중치의 합이 최소인 트리를 말한다.

입력

첫째 줄에 정점의 개수 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보다 작거나 같은 데이터만 입력으로 주어진다.

출력

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

풀이

1. MST를 구하는 문제는, 크루스칼과 프림알고리즘이 대표적이다. 그 중 프림 알고리즘 공부를 위해 프림 알고리즘을 택했다.

어려웠던 점, 알게된 것

1. 우선순위 큐에서 꺼내는 과정에서 = 그것은 곧 방문을 의미한다고 생각했다. 따라서, 방문처리는 따로하지 않았다.

2. 하지만, 방문처리를 반드시 해줘야 한다. 

3. 왜? 우선순위 큐에는 이전 방문한 노드들을 포함한 인접한 노드들이 모두 들어 있기 때문에 한 참 지나쳐온 가중치가 낮은 노드가 꺼내질 수 있다. 

관련 코드 

while(!pq.isEmpty()) {
			
			// 꺼내고 
			node cur = pq.poll();
			
			// 그굿이 방문한 곳이라면 패스 !  ** 이부분이 꼭 있어야한다. why? 큐를 꺼내는과정에서 예전에 넣어놨던 친구들이 나올 수 가 있잖아 !
			if( visited[cur.vertex]) continue;
			
			// 방문처리
			visited[cur.vertex] = true;
			
			// 방문했으니까, 가중치 더해주기
			answer += cur.weight;
			
			for ( node next : arr[cur.vertex]) {
				if ( !visited[next.vertex]) {
					pq.add(next);
				}
			}
			if ( ++ count == Vertex )break;

 

최종 정답 코드 

package 알고리즘공부하기;

import java.io.*;
import java.util.*;



public class 프림알고리즘_1197 {

	static int Vertex, Edge;
	static int start = 1;
	static ArrayList<node>[] arr; 
	static PriorityQueue<node> pq = new PriorityQueue<>();
	static boolean[] visited;
	static long answer =0;
	
	public static class node implements Comparable<node>{
		int vertex;
		int weight;
		
		public node(int vertex, int weight) {
			this.vertex = vertex;
			this.weight = weight;
		}
		
		@Override
		public int compareTo(node o) {
			return this.weight - o.weight;
		}
	}
	
	public static void main(String[] args) throws IOException {
		
		Scanner sc =new Scanner(System.in);
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		
		st = new StringTokenizer(br.readLine()," ");
		Vertex = Integer.parseInt(st.nextToken());
		Edge = Integer.parseInt(st.nextToken());
		
		visited	 = new boolean[Vertex+1]; // 각 정점은 1부터 시작한다.
		
		arr = new ArrayList[Vertex+1];
		for ( int i=0; i<=Vertex; i++) {
			arr[i] = new ArrayList<>();
		}
		
		// 정점간 연결 
		for( int i=0; i<Edge; i++) {
			st = new StringTokenizer(br.readLine(), " ");
			int from = Integer.parseInt(st.nextToken());
			int to = Integer.parseInt(st.nextToken());
			int weight = Integer.parseInt(st.nextToken());
			
			arr[from].add(new node(to,weight));
			arr[to].add(new node(from,weight));
		}
		
		
		// 시작점은 아무곳이나 상관 없지만, 1번 부터 시작하자.
		prim();
		
		System.out.println(answer);
	}
	private static void prim() {
		
		// 1번부터 시작해서 연결되어 있는 곳 모두 탐색하자.
		pq.add(new node(start,0));
		int count =0;
		while(!pq.isEmpty()) {
			
			// 꺼내고 
			node cur = pq.poll();
			
			// 그굿이 방문한 곳이라면 패스 !  ** 이부분이 꼭 있어야한다. why? 큐를 꺼내는과정에서 예전에 넣어놨던 친구들이 나올 수 가 있잖아 !
			if( visited[cur.vertex]) continue;
			
			// 방문처리
			visited[cur.vertex] = true;
			
			// 방문했으니까, 가중치 더해주기
			answer += cur.weight;
			
			for ( node next : arr[cur.vertex]) {
				if ( !visited[next.vertex]) {
					pq.add(next);
				}
			}
			if ( ++ count == Vertex )break;
			
			
			
		}
		
	}
	
	
}
반응형