코드굽는 타자기

Jungol[1863] - 종교 본문

알고리즘/DisjointSet(Union-Find)

Jungol[1863] - 종교

bright-jun 2020. 5. 2. 03:10

링크

 

JUNGOL | 종교 > 문제은행

정수 n , m 이 주어진다. 다음 m 라인은 두 정수 i , j 가 주어진다. i, j 는 i번 학생과 j번 학생이 같은 종교를 가진 학생의 쌍이다(1≤i, j≤n).

www.jungol.co.kr

문제설명

  • DisjointSet 개수 구하기

문제풀이

  • DisjointSet 개수 구하기

문제코드

import java.util.Scanner;

public class Main1863_종교 {

    public static int[] parent;

    /** 음수면 root이면서 rank수 이므로 -1로 초기화 */
    public static void makeSet(int v) {
//        자기 자신을 가르킴
        parent[v] = -1;
    }

    /** 포함된 집합의 root를 찾는다. */
    public static int findSet(int v) {
//      root인 경우
        if (parent[v] < 0) {
            return v;
        }
//        경로압축 - find하는 과정에서 거치는 노드들을 모두 parent[v]를 향하도록 만들어준다.
        else {
            parent[v] = findSet(parent[v]);
        }
//        경로 압축이 끝난 후, 부모반환
        return parent[v];
    }

    /** 두 트리를 결합한다. */
    public static void union_normal(int v1, int v2) {
//        v1 <- v2 병합
//        root = parent
        int root1 = findSet(v1);
        int root2 = findSet(v2);

//      두 원소를 포함한 집합이 같은 집합인 경우
        if (root1 == root2) {
            return;
        }

//        v1 <- v2 병합
        parent[root2] = root1;
    }

    /** 두 트리를 결합한다. */
//    집합에 포함된 노드의 개수를 rank로 두고 union하는 경우
    public static void union_by_weight(int v1, int v2) {
//        root = parent
        int root1 = findSet(v1);
        int root2 = findSet(v2);
//        rank = depth
        int rank1 = parent[root1];
        int rank2 = parent[root2];

//      두 원소를 포함한 집합이 같은 집합인 경우
        if (root1 == root2) {
            return;
        }

//        rank : 둘다 음수임, rank가 깊은 녀석에게 병합
//        rank v1 이 더 깊음 : v1 <- v2 병합
//        같은 경우는 둘 중 아무나 병합해도 상관없음 : v1 <- v2 병합
        if (rank1 <= rank2) {
            parent[root1] += parent[root2];
            parent[root2] = root1;
        }
//        rank v2 이 더 깊음 : v2<-v1 병합
        else if(rank1 > rank2){
            parent[root2] += parent[root1];
            parent[root1] = root2;
        }
    }

    /** 두 트리를 결합한다. */
//    집합의 height(최대 노드 depth)를 rank로 두고 union하는 경우
    public static void union_by_height(int v1, int v2) {
//        root = parent
        int root1 = findSet(v1);
        int root2 = findSet(v2);
//        rank = depth
        int rank1 = parent[root1];
        int rank2 = parent[root2];

//      두 원소를 포함한 집합이 같은 집합인 경우
        if (root1 == root2) {
            return;
        }

//        rank : 둘다 음수임, rank가 깊은 녀석에게 병합
//        rank v1 이 더 깊음 : v1 < -v2 병합
        if (rank1 < rank2) {
//            parent[root1] 의 height는 그대로 유지
            parent[root2] = root1;
        }
//        rank v2 이 더 깊음 : v2<-v1 병합
        else if(rank1 > rank2){
//            parent[root2] 의 height는 그대로 유지
            parent[root1] = root2;
        }
//        같은 경우는 둘 중 아무나 병합해도 상관없음 : v1 <- v2 병합
//        대신 병합되는쪽의 rank가 하나 증가함.
        else if(rank1 == rank2) {
//            parent[root1] 의 height는 하나 증가
            parent[root1] -= 1;
            parent[root2] = root1;
        }
    }

    public static int getRank(int v) {
        return -parent[findSet(v)];
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int n = sc.nextInt();
        int m = sc.nextInt();

//        tree node 생성
        parent = new int[n+1];
        for (int i = 1; i <= n; i++) {
            makeSet(i);
        }

//        Union
        for (int i = 0; i < m; i++) {
            int a = sc.nextInt();
            int b = sc.nextInt();
//            union_normal(a, b);
//            union_by_height(a, b);
            union_by_weight(a, b);
        }

//        Find Root
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            if(parent[i]<0) ans++;
        }

        System.out.println(ans);
    }
}

잘한 점

  • DisjointSet 공부하였음

효율성

  • 경로 압축 유무
  • Union 할 경우 Rank(Height or Weight)를 기준으로 Union 하는 것 유무
경로압축 Union 방식 메모리 시간
X X stack overflow -
O X 83MB 1295ms
X Weight 83MB 1213ms
O Weight 83MB 1198ms
X Height 82MB 1241ms
O Height 84MB 1227ms

결론

  • 경로 압축의 유무, Union 방식 둘 다 효율성에 영향을 크게 미친다.
    • 둘 중에 하나만 적용해도 충분하다.
  • Union방식에 있어서 Weight, Height 기준의 효율성 차이는 별로 없다.
  • 호텔 방 배정 같은 문제는 경로 압축만 이용해도 풀린 이유가 있다.
  • 둘 다 중요함

'알고리즘 > DisjointSet(Union-Find)' 카테고리의 다른 글

Programmers[43162] - 네트워크[Level3]  (0) 2020.05.03
Baekjoon[14868] - 문명  (0) 2020.05.02
Comments