ALGORITHM/BOJ

[백준] 11437, 11438 LCA (2)

0298 2020. 12. 7. 22:50

www.acmicpc.net/problem/11438

 

11438번: LCA 2

첫째 줄에 노드의 개수 N이 주어지고, 다음 N-1개 줄에는 트리 상에서 연결된 두 정점이 주어진다. 그 다음 줄에는 가장 가까운 공통 조상을 알고싶은 쌍의 개수 M이 주어지고, 다음 M개 줄에는 정

www.acmicpc.net

www.acmicpc.net/problem/11437

 

11437번: LCA

첫째 줄에 노드의 개수 N이 주어지고, 다음 N-1개 줄에는 트리 상에서 연결된 두 정점이 주어진다. 그 다음 줄에는 가장 가까운 공통 조상을 알고싶은 쌍의 개수 M이 주어지고, 다음 M개 줄에는 정

www.acmicpc.net

2020-12-07


 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
 
public class Main11438 {
    public static int N, M, dp[][], depth[];
    public static List<List<Integer>> list;
    public static boolean vtd[];
    public static int maxLevel = 21;
 
    public static void dfs(int cur, int parent) {
        vtd[cur] = true//방문 노드 체크
        //현재노드의 depth는 부모노드의 depth+1 이어야한다.
        //이 때, root node의 depth를 1로 잡는다
        depth[cur] = depth[parent] + 1;  
 
        for(int next: list.get(cur)) { // 현재 node와 연결된 노드들을 찾아서 depth체크, dp 부모값에 저장한다.
            if(!vtd[next]) { //방문한적이 없는 노드인 경우에만 체크한다.
                vtd[next] = true// 방문 체크 true 
                dp[next][0= cur; // 다음 갈 노드의 부모노드는 현재 노드 값이다.
                dfs(next, cur); //next값이 현재값이 되고, 현재 값이 부모값이 된다.
            }
        }
    }
 
    public static void solve() {
        for(int y = 1; y <= maxLevel; y++) { // 1 -> maxLevel
            for(int x = 1; x <= N; x++) { // 1 -> N 노드까지 체크
                dp[x][y] = dp[dp[x][y-1]][y-1];
            }
        }
    }
 
    public static int lca(int x, int y) {
        //x를 더 깊은 depth로 가정한다.
        //그래서 만약 y의 depth가 더 깊은 경우, x, y를 swap 한다.
        if(depth[x] < depth[y]) { 
            int tmp = y;
            y = x;
            x = tmp;
        }
 
        for(int i = maxLevel; i >= 0; i--) {
            //두 노드의 depth 차이가 2^i보다 작거나 같을 때...
            //x를 y의 depth에 맞춘다 
            if(Math.pow(2, i) <= depth[x] - depth[y]) {
                x = dp[x][i];
            }
        }
 
        if(x == y) return x; //두 노드의 부모 노드가 같다면? 그대로 return x
        else { //depth는 같으나 부모노드가 다르다면? 
            for(int i = maxLevel; i >= 0; i--) {
                //현재 같은 depth를 갖고 있기 때문에, 둘이 같은 공통 분모를 만날 수 있도록 레벨을 체크한다.
                if(dp[x][i] != dp[y][i]) {
                    x = dp[x][i];
                    y = dp[y][i];
                }
            }
            x = dp[x][0]; //공통 부모 직전까지 체크했으므로, 그 노드의 부모 노드를 return한다.
        }
        return x;
    }
 
    public static void main(String[] args) throws Exception {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(bf.readLine().trim());
        N = Integer.parseInt(st.nextToken());
        // root 노드를 1부터 시작
        dp = new int[N+1][maxLevel+1]; // dp[x][y] 형태로 x의 2^y 부모노드를 저장
        depth = new int[N+1]; // 각 노드의 부모 노드 저장 배열
        vtd = new boolean[N+1]; // dfs 돌 때 방문 체크 배열
        list = new ArrayList<>();
 
        for(int i = 0; i <= N; i++) list.add(new ArrayList<>()); //초기화
 
        for(int i = 0; i < N-1; i++) { // 입력
            st = new StringTokenizer(bf.readLine());
            int x = Integer.parseInt(st.nextToken());
            int y = Integer.parseInt(st.nextToken());
            list.get(x).add(y);
            list.get(y).add(x);
        } 
 
        dfs(10); // dfs로 노드들을 순회하면서, 각각의 depth를 찾고, 부모노드들을 저장한다.
        solve(); // dp를 활용하여, 각 노드의 2^i 부모노드들을 저장한다.
 
        st = new StringTokenizer(bf.readLine().trim());
        M = Integer.parseInt(st.nextToken());
 
        for(int i = 0; i < M; i++) {
            st = new StringTokenizer(bf.readLine());
            int x = Integer.parseInt(st.nextToken());
            int y = Integer.parseInt(st.nextToken());
            System.out.println(lca(x, y));
        }
    }
}
cs

여러 블로그들을 통해 LCA 개념에 대해서 공부한 후 풀었다. 

자세한 것은 주석을 확인하면 된다.