[ 최소 공통 조상 ] 알고리즘

2023. 8. 23. 01:55개발/👾 PS

 

 

1.  개념

     

     최소 공통 조상 : 트리에서 임의의 두 노드가 처음 공통으로 갖게 되는 조상 노드

          

 

 

2.  구현

       

      < 1 >  일반적인 최소 공통 조상 구하기

                

                일반적인 방법은 트리의 높이가 별로 높지 않을 때 이용한다.

                

                1 )  부모 노드, 깊이 리스트 생성 

                 트리의 루트 노드에서 탐색을 시작하여 각 노드의 부모 노드와 깊이를 저장한다.

                 이때 탐색은 DFS 혹은 BFS를 이용한다.

                  

                2 ) 깊이 맞추기

                 더 깊은 노드를 부모 노드로 올려 깊이를 맞춰준다.

                 깊이를 맞춰주었을 때 두 노드가 서로 같으면 해당 노드가 최소 공통 조상이 된다.

                  

                3 ) 최소 조상 노드 찾기

                 깊이가 같아진 두 노드를 함께 부모 노드로 올려준다.

                 두 노드가 서로 같아질 때까지 반복한다.

                 두 노드가 서로 같아지면 해당 노드가 최소 공통 조상이 된다.

               

                하지만 이 방법은 시간 제약에 약하기 때문에 좀 더 빠르게 계산할 수 있는 방법을 알아야 한다.

 

 

      < 2 >  최소 공통 조상 빠르게 구하기

                

                위에서 이용했던 방식을 그대로 가져가되 부모 노드로 한 단계씩 올려주는 것이 아니라

                2ᵏ번째 조상 노드로 올려주면 된다.

                

                1 )  부모 노드 저장 리스트 생성

                부모 노드 저장 리스트의 크기는 K × N으로 

                N은 노드의 개수를 의미하며

                K는 트리의 깊이 > 2ᴷ 를 만족하는 K의 최댓값으로 정한다.

 

                 부모 노드 저장 리스트 P에서 P[k][n]는 n번 노드의 2ᵏ번째 조상을 의미한다. 

                 부모 노드 저장 리스트를 생성할 땐 아래 점화식을 이용하면 된다.

                 P[k][n] = P[k-1][P[k-1][n]]

                 

                2 )  선택된 두 노드의 깊이 맞추기

                 더 깊은 노드를 (두 노드의 깊이 차이)번째 조상으로 올려준다.

                

                3 )  최소 공통 조상 찾기

                 두 노드에 대하여 P[K][n]에서 출발하여 K값을 1씩 감소하면서

                 두 노드의 부모 노드가 서로 달라지는 시점으로 두 노드를 옮겨준다.

                 

                 마지막으로 최초로 부모 노드가 달라진 두 노드의 부모 노드를 찾아서 이동해주면 된다.

                 

                  

 

3.  문제

 

# 1  최소 공통 조상 구하기 1 - 백준 11437번

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

 

11437번: LCA

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

www.acmicpc.net

 

 

import sys
from collections import deque
input = sys.stdin.readline
print = sys.stdout.write

n = int(input())
tree = [[] for _ in range(n+1)]

# 트리 생성 -> 인접리스트
for _ in range(0, n-1):
    s, e = map(int, input().split())
    tree[s].append(e)
    tree[e].append(s)

# 부모, 깊이 리스트 생성 -> BFS
depth = [0] * (n+1)
parent = [0] * (n+1)
visited = [False] * (n+1)


def BFS(node):
    queue = deque()
    queue.append(node)
    visited[node] = True
    level = 1        # 깊이
    now_size = 1     # 현재 깊이에서의 트리 크기
    count = 0        
    while queue:
        now_node = queue.popleft()
        # now_node : 현재 노드(부모 노드) / next : now_node의 자식 노드들
        for next in tree[now_node]:
            if not visited[next]:
                visited[next] = True
                queue.append(next)
                parent[next] = now_node    # 부모 노드 저장
                depth[next] = level        # 깊이 저장
        count += 1
        
        # 현재 깊이에 존재하는 노드 탐색이 끝나면
        if count == now_size:
            count = 0
            # 큐에 들어가있는 노드(now_node의 자식 노드) 개수 = 다음 깊이의 트리 크기
            now_size = len(queue)
            # 깊이 갱신
            level += 1


BFS(1)    # BFS에 루트 노드 전달


def executeLCA(a, b):
    # 깊이 맞춰주기
    if depth[a] < depth[b]:
        a, b = b, a
    while depth[a] != depth[b]:
        a = parent[a]
    
    # 최소 공통 조상 찾기
    while a != b:
        a = parent[a]
        b = parent[b]
    
    return a

m = int(input())

for i in range(m):
    a, b = map(int, input().split())
    print(str(executeLCA(a, b)))
    print("\n")

 

 

 

# 2  최소 공통 조상 구하기 2 - 백준 11438번

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

 

11438번: LCA 2

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

www.acmicpc.net

'개발 > 👾 PS' 카테고리의 다른 글

🐣브실컵🐣  (0) 2023.09.11
🍉 2023 SUAPC SUMMER 🍉  (0) 2023.09.11
[ 세그먼트 트리 ] 알고리즘  (0) 2023.08.23
📣 솔브드 그랜드 아레나 📣  (0) 2023.08.16
[ 이진 트리 ] 알고리즘  (0) 2023.07.26