最小拡張ツリー(MST)


📌さいしょうのびじゅ


💡 グラフィックでの最低コストの問題

  • すべての頂点を結ぶ幹線の重みと最小の木(최소 신장 트리)
  • 両頂点間の最小コストパスを探す(최단 경로)
  • 腎臓の木

  • n個の頂点からなる無方向図中n개의 정점n-1개의 간선からなる木
  • どの頂点も孤立しないようにつながっている木.BUTサイクルはできません!
  • 最小長ツリー

  • 無重み付け図における腎樹を構成する幹線の重み付けと最小の腎樹
  • 📌KRUSKALアルゴリズム

  • 一本幹線でMSTを探すアルゴリズム
  • 幹線を使用しているので리스트
  • 💡 アルゴリズム#アルゴリズム#

  • 最初はすべての幹線が重み付け順に昇順
  • 最も重みの低い幹線から選び、木を増やします.
    「100サイクル」がある場合は、次の重みの低い幹線を選択します.
  • N-1幹線が選択されるまで2を繰り返す.

  • 💡 アルゴリズム適用例



    💡 コード#コード#

    public class Kruskal {
    
    	static class Edge implements Comparable<Edge> {
    
    		int start, end, weight;
    
    		public Edge(int start, int end, int weight) {
    			super();
    			this.start = start;
    			this.end = end;
    			this.weight = weight;
    
    		}
    
    		@Override
    		public int compareTo(Edge o) { // compareTo 나를 기준으로 o를 비교
    			//return this.weight-o.weight; //간선의 부호가 모두 같을 때
    			return Integer.compare(this.weight, o.weight);
    		}
    
    	}
    
    	static int[] parents; // 부모원소를 관리(트리처럼 사용)
    
    	private static void make() {
    		parents = new int[V];
    		// 모든 원소를 자신을 대표자로 만듦
    		for (int i = 0; i < V; i++) {
    			parents[i] = i;
    		}
    	}
    
    	// a가 속한 집합의 대표자 찾기
    	private static int find(int a) {
    		if (a == parents[a]) return a; // 자신이 대표자.
    		return parents[a] = find(parents[a]); // 자신이 속합 집합의 대표자를 자신의 부모로 : path compression
    	}
    
    	// 두 원소를 하나의 집합으로 합치기(대표자를 이용해서 합침)
    	private static boolean union(int a, int b) {
    		int aRoot = find(a);
    		int bRoot = find(b);
    		if (aRoot == bRoot) return false; // 이미 같은 집합으로 합치지 않음
    
    		parents[bRoot] = aRoot;
    		return true;
    	}
    
    	static int V, E;
    	static Edge[] edgeList;
    
    	public static void main(String[] args) throws Exception {
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		StringTokenizer st = new StringTokenizer(br.readLine());
    
    		V = Integer.parseInt(st.nextToken());
    		E = Integer.parseInt(st.nextToken());
    
    		// 간선리스트 작성
    		edgeList = new Edge[E];
    
    		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 weight = Integer.parseInt(st.nextToken());
    			edgeList[i] = new Edge(start, end, weight);
    
    		}
    		Arrays.sort(edgeList); // 오름차순 정렬
    
    		make(); // 모든 정점을 각각의 집합으로 만들고 출발
    
    		// 간선 하나씩 시도하며 트리 만들어 감.
    		int cnt = 0, result = 0;
    		for (Edge edge : edgeList) {
    			if (union(edge.start, edge.end)) {
    				// 간선을 이어봐!!
    				result += edge.weight;
    				if (++cnt == V - 1) break; // 간선이 v-1개가 되면 신장 트리 완성
    			}
    		}
    
    		System.out.println(result);
    	}
    
    }

    📌PRIMアルゴリズム

  • 一つの頂点に接続された幹線の中から一つを選択してMSTを作成する方式
  • 頂点中心なので使用인접행렬, 인접 리스트
  • 💡 アルゴリズム#アルゴリズム#

  • 任意の頂点から選択
  • 選択した頂点に隣接する頂点のうち最小コストラインが存在する頂点を選択
  • すべての頂点が選択されるまで1、2つのプロセスを繰り返す
  • 幹線が多いと、Primはkruskalより効率的です.
  • kruskalは幹線を揃える必要があるためprimより効率が低い可能性がある.
  • 💡 アルゴリズム適用例




    💡 コード#コード#

    public class PrimTest {
    
    	public static void main(String[] args) throws Exception {
    
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		int N = Integer.parseInt(br.readLine());
    
    		int[][] adjMatrix = new int[N][N]; //인접행렬
    		boolean[] v = new boolean[N];
    		int[] minEdge = new int[N];
    
    		for (int i = 0; i < N; i++) {
    			StringTokenizer st = new StringTokenizer(br.readLine());
    			for (int j = 0; j < N; j++) {
    				adjMatrix[i][j] = Integer.parseInt(st.nextToken());
    			}
    			minEdge[i] = Integer.MAX_VALUE;
    		}
    
    		int result = 0;
    		minEdge[0] = 0; // 임의의 시작점 0의 간선비용을 0으로 세팅
    
    		// 
    		for (int i = 0; i < N; i++) {
    			// 1. 신장트리에 포함되지않은 정점 중 최소간선비용의 정점 찾기
    			int min = Integer.MAX_VALUE;
    			int minVertex = -1; // 최소 간선비용의 정점번호
    
    			
    			for (int j = 0; j < N; j++) {
    				if (!v[j] && min > minEdge[j]) {
    					min = minEdge[j];
    					minVertex = j;
    				}
    			}
    
    			v[minVertex] = true; // 신장트리에 포함 시킴
    			result += min; // 간선비용 누적
    
    			// 2. 선택된 정점 기준으로 신장트리에 연결되지 않은 타 정점과의 간선 비용 최소로 업데이트
    
    			for (int j = 0; j < N; j++) {
    				if (!v[j] && adjMatrix[minVertex][j] != 0 && minEdge[j] > adjMatrix[minVertex][j]) 
    					minEdge[j] = adjMatrix[minVertex][j];
    			}
    
    		}
    
    		System.out.println(result);
    	}
    
    }