알고리즘||코딩테스트/LCA

[BOJ 3584] 가장 가까운 공통 조상 - JAVA, Gold4

째로스 2023. 9. 9. 22:24

https://www.acmicpc.net/problem/3584

 

3584번: 가장 가까운 공통 조상

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

www.acmicpc.net

사용한 알고리즘 : LCA(Lowest Common Ancestor, 최소 공통 조상)

 

풀이 전략

위 문제에서 주어진 첫 번째 입력을 그래프로 나타내면 아래와 같다.

문제에서는 16과 7의 가장 가까운 공통 조상인 LCA를 구하라고 주어졌다.

결론부터 말하면 눈에 보이듯 4인데, 이를 알고리즘적으로 어떻게 구할 수 있을지 생각해보자.

 

먼저 LCA를 찾을 각 노드인 16과 7이 루트 노드까지 어떻게 올라가는지 탐색한다.

16은 16->10->4->8

7은 7->6->4->8

경로로 루트 노드까지 이어진다.

 

경로 상의 각 노드들은 16과 7 노드의 조상 노드들이고

결과적으로는 둘 다 루트 노드를 최상위 부모 노드로 갖는다.(둘 다 같은 트리에 속한 노드이므로)

 

그럼 여기서 어떻게 가장 가까운 공통 조상 노드를 찾을 수 있을까?

여러 방법이 있겠지만 나는 스택을 사용했다.

 

후입 선출의 구조를 지닌 스택 구조를 활용했는데,

각 노드가 루트 노드까지 올라가는 과정의 노드들을 스택에 차례로 push한다.

 

그러면 16과 7의 조상 노드들이 아래 그림처럼 Stack에 쌓이게 된다.

최초 상태에서의 두 스택의 꼭대기 값은 루트 노드의 번호이다.(위 그림에서는 8)

그리고 그 이후에 pop한 값이 서로 일치한다는 것은 두 수가 공통된 조상 노드를 가진다는 의미이다.

 

8 = 8   --> 공통 노드

4 = 4   --> 공통 노드

10 != 6  --> 공통 노드 아님

 

따라서 두 수를 비교했을 때 공통된 수가 아니게 될 때까지 비교를하면

마지막으로 공통됐던 값이 LCA가 된다.(위 그림에서는 4)

 

제출코드

import java.io.*;
import java.util.Stack;
import java.util.StringTokenizer;

public class Main {

    static int parent[];
    static Stack<Integer> stackA = new Stack<>();
    static Stack<Integer> stackB = new Stack<>();

    static int find_parent(int x, Stack<Integer> stack){
        stack.push(x);
        if(x==parent[x]) return x;
        else return find_parent(parent[x],stack);
    }

    static int find_LCA(){
        int lca=0;
        while(!stackA.isEmpty() && !stackB.isEmpty()){
            int aVal = stackA.pop();
            int bVal = stackB.pop();
            if(aVal==bVal){
                lca=aVal;
            }
            else break;
        }
        return lca;
    }
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st;

        int T = Integer.parseInt(br.readLine());
        for(int t=0;t<T;++t){
            int N = Integer.parseInt(br.readLine());
            parent = new int[N+1];
            stackA.clear();
            stackB.clear();
            for(int i=1;i<=N;++i){
                parent[i]=i;
            }

            for(int n=0;n<N-1;++n){
                st = new StringTokenizer(br.readLine());
                int A = Integer.parseInt(st.nextToken());
                int B = Integer.parseInt(st.nextToken());
                parent[B]=A;
            }

            st = new StringTokenizer(br.readLine());
            int A = Integer.parseInt(st.nextToken());
            int B = Integer.parseInt(st.nextToken());

            find_parent(A,stackA);
            find_parent(B,stackB);

            bw.write(find_LCA()+"\n");
        }
        bw.flush();
        bw.close();
    }
}