N개의 도시와 그 도시들을 연결하는 N-1개의 도로로 이루어진 도로 네트워크가 있다. 모든 도시는 유일한 경로로 연결되어 있으며, 각 도로의 길이는 입력으로 주어진다.
총 K개의 도시 쌍이 주어질 때, 각 쌍에 대해 두 도시를 연결하는 경로 상에서 가장 짧은 도로의 길이와 가장 긴 도로의 길이를 구하는 프로그램을 작성해야 한다. 이 문제는 트리 구조에서 두 노드 사이의 경로를 탐색하면서, 경로 상의 간선 가중치 중 최소값과 최대값을 빠르게 구하는 것이 핵심이다.
이를 위해 트리에서 두 노드의 최소 공통 조상(LCA, Lowest Common Ancestor)을 찾는 알고리즘과 Sparse Table을 활용하여 쿼리를 효율적으로 처리해야 한다. Binary Lifting 기법을 사용하여 각 노드의 2^k번째 조상을 미리 계산해 두면, 두 노드의 LCA를 O(log N) 시간에 찾을 수 있다.
또한, 각 노드에서 조상 노드까지의 경로 상의 최소 및 최대 간선 가중치를 저장해 두면, 두 노드 사이의 경로에서 최소값과 최대값을 구하는 데에도 O(log N) 시간이 소요된다.
문제 : https://www.acmicpc.net/problem/3176
접근 방식
이 문제를 해결하기 위해 다음과 같은 알고리즘과 자료 구조를 사용하였다.
최소 공통 조상(LCA) 알고리즘: 트리에서 두 노드의 LCA를 찾기 위해 Binary Lifting 기법을 활용하였다. 이를 통해 두 노드의 LCA를 O(log N) 시간에 찾을 수 있다.
Sparse Table: 각 노드의 2^k번째 조상을 미리 계산해 두는 테이블을 구축하였다. 또한, 각 노드에서 조상까지의 경로에서의 최소 및 최대 간선 가중치를 저장하여 쿼리를 효율적으로 처리하였다.
DFS(Depth-First Search): 트리를 탐색하면서 각 노드의 깊이(depth)와 부모(parent)를 기록하였다.
Binary Lifting: 각 노드의 2^k번째 조상을 빠르게 찾기 위해 Binary Lifting을 사용하였다.
이를 통해 각 쿼리에 대해 O(log N)의 시간 복잡도로 답을 구할 수 있으며, 전체 시간 복잡도는 O(N log N + K log N)이다.
C++ 코드와 설명
|
|
코드 설명
데이터 입력 및 초기화:
- 도로 정보를 입력받아 인접 리스트
adj
에 저장한다. memset
함수를 사용하여 배열을 초기화한다.anc
배열을-1
로 초기화하여 초기 상태를 설정한다.min_t
배열은 큰 값으로,max_t
배열은0
으로 초기화한다.
- 도로 정보를 입력받아 인접 리스트
DFS를 통한 깊이 및 조상 설정:
dfs
함수에서 각 노드의 깊이와 부모를 설정하고, 간선의 가중치를min_t
와max_t
에 저장한다.- 재귀적으로 트리를 탐색하며 정보를 수집한다.
Sparse Table 구축:
- 이중 반복문을 통해 각 노드의
2^k
번째 조상을 계산하고, 해당 경로에서의 최소 및 최대 간선 가중치를 갱신한다. anc[v][k]
,min_t[v][k]
,max_t[v][k]
를 업데이트한다.
- 이중 반복문을 통해 각 노드의
쿼리 처리:
- 입력받은 두 노드의 깊이를 맞추기 위해 깊이가 더 깊은 노드를 위로 올린다.
- 깊이를 맞춘 후, 두 노드가 같아질 때까지
k
를 감소시키며 조상을 찾아간다. - 이 과정에서 최소값과 최대값을 갱신한다.
- 최종적으로 LCA의 바로 아래 간선까지 고려하여 결과를 출력한다.
위의 코드는 컴파일 에러 없이 동작하며, 주어진 입력에 대해 올바른 출력을 생성합니다.
C++ without library 코드와 설명
stdio.h
와 malloc.h
만을 사용하여 표준 라이브러리를 사용하지 않고 구현한 코드이다.
|
|
코드 설명
인접 리스트 구현:
- 표준 라이브러리를 사용할 수 없으므로,
Edge
구조체와 연결 리스트를 사용하여 인접 리스트를 구현하였다. add_edge
함수를 통해 간선을 추가한다.
- 표준 라이브러리를 사용할 수 없으므로,
DFS 구현:
dfs
함수에서 재귀적으로 트리를 탐색하며 깊이와 조상을 설정하고, 간선 가중치를 저장한다.
Sparse Table 구축 및 쿼리 처리:
- 최소값과 최대값을 갱신할 때 표준 라이브러리의
min
,max
함수를 사용할 수 없으므로, 조건문을 사용하여 직접 구현하였다. - 쿼리 처리 로직은 앞서 설명한 것과 동일하다.
- 최소값과 최대값을 갱신할 때 표준 라이브러리의
결론
이 문제는 트리에서 두 노드 사이의 경로에서 최소 및 최대 간선 가중치를 빠르게 구해야 하는 전형적인 LCA 응용 문제이다. Binary Lifting과 Sparse Table을 사용하여 각 노드의 조상 정보를 미리 계산해 두면, 쿼리를 O(log N)에 처리할 수 있다.
이를 통해 시간 복잡도를 효율적으로 관리하여 큰 입력에서도 빠르게 답을 구할 수 있었다. 또한, C++ 표준 라이브러리를 사용하지 않고도 알고리즘을 구현하여 언어의 기본 기능만으로도 충분히 문제를 해결할 수 있음을 확인하였다.
추가적으로, 이와 유사한 유형의 문제를 많이 풀어봄으로써 트리에 대한 이해와 알고리즘 구현 능력을 향상시킬 수 있을 것이다. 최적화 측면에서는 메모리 사용량을 줄이거나 더 효율적인 자료 구조를 활용하는 방안을 고려해 볼 수 있다.