문제 풀이

[백준] 3548번 가장 가까운 공통 조상

컴영 2020. 8. 31. 17:58

3584 가장 가까운 공통 조상


문제


루트가 있는 트리(rooted tree)가 주어지고, 그 트리 상의 두 정점이 주어질 때 그들의 가장 가까운 공통 조상(Nearest Common Anscestor)은 다음과 같이 정의됩니다.

-두 노드의 가장 가까운 공통 조상은, 두 노드를 모두 자손으로 가지면서 깊이가 가장 깊은(즉 두 노드에 가장 가까운) 노드를 말합니다.

nca.png

예를 들어  15와 11를 모두 자손으로 갖는 노드는 4와 8이 있지만, 그 중 깊이가 가장 깊은(15와 11에 가장 가까운) 노드는 4 이므로 가장 가까운 공통 조상은 4가 됩니다.

루트가 있는 트리가 주어지고, 두 노드가 주어질 때 그 두 노드의 가장 가까운 공통 조상을 찾는 프로그램을 작성하세요


입력


첫 줄에 테스트 케이스의 개수 T가 주어집니다.

각 테스트 케이스마다, 첫째 줄에 트리를 구성하는 노드의 수 N이 주어집니다. (2 ≤ N ≤ 10,000)

그리고 그 다음 N-1개의 줄에 트리를 구성하는 간선 정보가 주어집니다. 한 간선 당 한 줄에 두 개의 숫자 A B 가 순서대로 주어지는데, 이는 A가 B의 부모라는 뜻입니다. (당연히 정점이 N개인 트리는 항상 N-1개의 간선으로 이루어집니다!) A와 B는 1 이상 N 이하의 정수로 이름 붙여집니다.

테스트 케이스의 마지막 줄에 가장 가까운 공통 조상을 구할 두 노드가 주어집니다.


출력


각 테스트 케이스 별로, 첫 줄에 입력에서 주어진 두 노드의 가장 가까운 공통 조상을 출력합니다.



풀이)

단순히 어떤 노드끼리 연결되었는지 정보를 준 것이 아니라 부모-자식 노드 관계 정보를 준 문제여서

주어진 정보를 이용해 그대로 트리를 만들어 풀면 된다.


과정

1. 트리를 만든다.


(사용하는 배열들)

int parent[i] // i 노드의 부모 노드 번호 저장하는 배열

int depth[i] // i 노드의 트리 내에서의 깊이 저장하는 배열


parent[]배열을 이용해 트리의 루트 노드 번호를 찾고, (루트 노드 번호는 그 노드의 부모 노드 번호와 일치할때의 번호)

그리고 그 루트 노드부터 시작해 dfs로 각 노드들 깊이들을 확인해 depth[]에 저장


ex) 트리 입력 예시

n = 5

(2 3)

(3 4)

(3 1)

(1 5)

(3 5) 일때,


2. 공통 조상을 구할 두 노드의 트리 내 깊이를 확인한다.


     노드가 n1, n2라고 할때 depth[n1] , depth[n2] 확인


3. 두 노드가 같은 깊이를 가질 때까지 한 노드를 그 노드의 부모로 바꿔준다.


4. 깊이가 같아졌을 때 노드 번호가 똑같은지 확인 

                             같은 번호가 아니라면, 두 노드들 모두 같은 번호가 될때까지 각자 부모 노드로 변경.


5. 같은 노드 번호(= 최소 공통 조상)를 출력



코드)


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
#include <stdio.h>
#include <vector>
#include <algorithm>
using namespace std;
 
int n;
vector <int> adj[10001];
int depth[10001];
int parent[10001];
 
void dfs(int node) {
    for (int i = 0; i < adj[node].size(); i++) {
        int next = adj[node][i];
        if (depth[next] == -1) {
            depth[next] = depth[node] + 1;
            dfs(next);
        }
    }
}
int main() {
    int test_case;
    scanf("%d"&test_case);
    while (test_case--)
    {
        scanf("%d"&n);
        int a, b;
        for (int i = 1; i <= n; i++) {
            parent[i] = i;
        }
        for (int i = 0; i < n-1; i++) {
            scanf("%d %d"&a, &b);
            parent[b] = a;
            adj[a].push_back(b);
        }
        int n1, n2;
        scanf("%d %d"&n1, &n2);
 
        for (int i = 1; i <= n; i++) {
            depth[i] = -1;
        }
 
        int root = 1;
        while (true)
        {
            if (root == parent[root]) break;
            root = parent[root];
        }
        depth[root] = 0;
        dfs(root);
        
        if (depth[n1] < depth[n2]) swap(n1, n2);//깊이가 깊은 노드를 n1으로
        
        while (depth[n1] != depth[n2]) //깊이가 같아질때까지
        {
            n1 = parent[n1];
        }
        if (n1 == n2) {
            printf("%d\n", n1);
            continue;
        }
        while (n1 != n2)
        {
            n1 = parent[n1];
            n2 = parent[n2];
        }
        printf("%d\n", n1);
 
        for (int i = 1; i <= n; i++) {
            parent[i] = -1;
            adj[i].clear();
        }
    }
    return 0;
}
cs