-
백준 11437번 LCA , 11438번 LCA2알고리즘 2023. 8. 2. 12:21728x90반응형SMALL
LCA란 Lowest Common Ancestor로 두 정점의 가장 가까운 공통 조상을 찾는 문제입니다
시도 1.
루트 노드인 1번 노드부터 연결된 간선을 통해 자식 노드들을 내려가게 됩니다. 이 때, 자식노드의 부모노드를 모두 저장하면서 내려가도록 합니다.
여기서 중요한 것은 주어지는 입력이 루트노드로부터 가까운 순이 아니므로, 모든 간선의 입력을 받은 뒤 부모노드와 자식 노드를 분간해야 된다는 것입니다.
모든 노드의 부모노드를 찾은 뒤, 두 정점으로부터 가까운 공통 부모를 찾아줍니다.
한 노드로부터 부모노드로 계속 이동하여 루트 노드까지 방문하고 방문한 노드에 표시를 해줍니다(visited 배열을 통해). 그 다음 다른 노드 또한 부모노드로 계속 이동하여 루트 노드까지 이동하는데
visited
배열에 표시된 노드가 나올 때까지만 이동해줍니다.만약 표시된 노드가 나올 경우 해당 노드가 가장 가까운 공통 부모가 되므로 해당 노드를 출력해줍니다.
def recursion(node): for newNode in edges[node]: if not visited[newNode]: visited[newNode]=True toParent[newNode]=node recursion(newNode) def visitParent(node): visited[node]=True if node==1: return visitParent(toParent[node]) for a,b in mList: answer=1 visited=[False for _ in range(N+1)] visitParent(a) findCommonParent(b) print(answer)
하지만, 결과는 시간 초과가 나왔습니다.
처음에는 우선 노드의 개수가 최대 50,000이고 공통 조상을 알고 싶은 노드 쌍이 최대 10,000개가 될 수 있지만, 트리의 depth는
log(노드 개수)
이기 때문에 적은 시간으로도 순회가 가능하고 그렇기 때문에 문제 없이 통과할 것이라고 생각했습니다.하지만, 이 문제와 같은 경우 항상 완전 이진 트리의 형태를 가지지 않으므로 트리의 depth는 최악의 경우 N이 될 수 있기 때문에, 최악의 경우 트리 전체를 두 번 순회하는 경우가 발생하므로 시간 초과가 발생한 것 같습니다.
시도 2.
각 노드에 이어진 간선을 모두 저장하는 것은 기존과 동일하게 진행하고, 각 노드의 부모노드와 각 노드의 depth를 저장해줍니다.
depth와 부모 노드를 구할 경우 dfs알고리즘을 이용해줍니다.
def dfs(node, depth): visited[node] = True d[node] = depth for newNode in graph[node]: if not visited[newNode]: parent[newNode] = node dfs(newNode, depth + 1)
그 다음 가장 가까운 공통 조상을 구해줍니다.
우선, 두 접점의 depth가 맞을 때까지 더 깊은 위치에 있는 접점을 부모로 이동시켜줍니다.
두 접점의 depth가 동일하다면 각 노드로부터 계속 부모로 이동하면서 동일 부모가 나올 때까지 이동시켜줍니다.
def lca(a, b): while d[a] != d[b]: if d[a] > d[b]: a = parent[a] else: b = parent[b] while a != b: a = parent[a] b = parent[b] return a
위와 같은 경우 최악의 경우에도 한 번만 트리를 순회하고 끝나기 때문에 이전 방법과는 달리 시간 초과가 발생하지 않은 것 같습니다.
11438 LCA2
이번 문제는 이전 문제에서 시간 제한이 좀 더 줄어든 문제입니다.
시간 제한이 줄어들었기 때문에 순회에 대한 시간을 줄여주어야 합니다.
이전 문제에서 부모로의 이동을 한 칸씩만 했기 때문에 순회에 시간이 오래 걸리게 되었습니다.
그렇기 때문에 한 칸씩 이동하는 것이 아닌 2의 제곱 칸으로 이동 할 수 있도록 메모이제이션을 해주도록 하겠습니다.
parent 2차 배열은 다음과 같이 수를 저장해주도록 합니다.
위와 같은 예시 트리가 있을 경우에 17에 대한 부모를 저장하는 방법을 다르게 해보도록 하겠습니다.
parent배열에 저장되는 부모는 2**i 번째 부모입니다. 즉,
parent[node][0]
은 node의 2**0인 0 번째 부모를 뜻하게 됩니다.그러므로
parent[17][0]
은 바로 앞의 부모인 14가 되고parent[17][1]
은 1 번째 부모인 9가 됩니다.이런식으로 모든 노드를 저장 할 경우 이동이 훨씬 빠르게 될 수 있다고 생각했습니다.
def set_parent(): recursion(1, 0) # 1번 노드부터 depth를 구해줍니다. for i in range(1, DEPTH): for j in range(1, n + 1): parent[j][i] = parent[parent[j][i - 1]][i - 1]
parent[j][i]
는 j노드의 2**(i-1) 노드의 i-1번째 부모입니다.위의 17번 노드에 대해서 예시를 들어보자면
parent[17][2]
는parent[17][1]
번째 노드인 9의 1 번째 부모입니다. 즉,parent[9][1]
입니다.lca 함수 변경
우선 두 접점의 깊이를 맞춰줍니다.
두 접점의 깊이 차이를 최대한 줄여줄 수 있는 길이를 이동 할 수 있도록(더 깊은 노드가 이동 했을 때, 덜 깊은 노드를 넘어서지 않도록), 문제에서 주어진 최대 깊이와 동일한 길이부터 0까지 내림차순으로 2**i만큼 이동 했을 때 다른 노드의 깊이보다 깊은 지역으로 이동 할 수 있는지 확인하고, 이동 할 수 있다면 이동을 해줍니다.
위의 사진을 예시로 들면 17과 3에 대해서 같은 깊이를 찾는 경우를 살펴보겠습니다.
더 깊이 있는 17을 이동시키는데, 3의 깊이까지 가장 길게 이동 할 수 있는 거리는 2**2입니다. 그러므로 17을 우선 2로 이동 시켜줌으로써 두 노드의 깊이를 맞추어줍니다.
만약, 17과 6이라면, 6의 깊이까지 가장 길게 이동 할 수 있는 거리는 2**1이므로 17을 9로 이동 시켜준 다음, 9를 6의 깊이까지 가장 길게 이동 할 수 있는 거리인 2**0으로 이동시켜줌으로써 4로 이동하여 6과 깊이를 맞춰줍니다.
깊이를 맞춰 이동한 두 접점이 같은 노드를 가리킨다면 해당 노드가 LCA가 됩니다.
이 후 두 노드를 parent 배열을 통해 가장 먼 부모노드부터 순회하면서 같은 부모를 가리키지 않을 때에만 이동시켜줌으로써 가장 가까운 공통 조상을 찾을 수 있도록 해줍니다.
for i in range(DEPTH - 1, -1, -1): if parent[a][i] != parent[b][i]: a = parent[a][i] b = parent[b][i]
위와 같이 이동 할 경우 두 노드는 공통 부모 바로 아래 자식에서 순회가 멈추게 됩니다.
그러므로 한 접점의 0 번째 부모가 가장 가까운 공통 조상이 됩니다.
이번 문제는 순회를 더 빠르게 할 수 있는 방법에 대해 찾아보는 것이 주요했던 문제입니다. 11438번과 같은 경우 제대로 찾지 못해 풀이를 참고하면서 풀게 되어 아쉬웠습니다. 다른 분의 풀이를 참고한만큼 제대로 문제를 파악하고 다음에 한 번 더 푸는 기회를 갖는 것이 이 문제를 제대로 학습하는 방법이라고 생각됩니다.
공통 조상을 찾으면서 이동하는 것을 항상 한 칸씩 이동하는 것보다는 위의 방식과 같이 메모이제이션을 이용하면 훨씬 빠르게 찾을 수 있다는 것을 알게 되었습니다. 또한, 메모이제이션을 이용한다면 메모리의 사용량은 증가하는 대신 시간 복잡도에서 우위를 가져갈 수 있지만, 사용하지 않고 문제를 풀게 된다면 메모이제이션을 이용했을 때의 장점은 단점으로 단점은 장점으로 가져가게 될 수 있다는 것도 다시금 생각 할 수 있게 해준 문제인 것 같습니다.
reference : https://www.youtube.com/watch?v=O895NbxirM8&t=473s
반응형LIST'알고리즘' 카테고리의 다른 글
백준 1937번 욕심쟁이 판다, 2437번 저울 (python) (2) 2023.08.04 백준 1202번 보석 도둑 (0) 2023.08.03 백준 2048 (Easy) (Gold 2) (0) 2023.07.27 백준 1167번 트리의 지름 (Gold 2) (0) 2023.07.26 백준 알고리즘 풀이 (Gold) 1753번, 1655번 (0) 2023.07.25