레야몬
[C++] 11438번 LCA 2 - 자료 구조, 트리, 최소 공통 조상, 희소 배열 본문
- 문제
- N(\(2 \leq N \leq 100,000\))개의 정점으로 이루어진 트리가 주어진다. 트리의 각 정점은 1부터 N가지 번호가 매겨져 있으며 루트는 1이다.
- 각 노드의 쌍 M(\(1 \leq M \leq 100,000\))개가 주어졌을 때 두 노드의 가장 가까운 공통 조상이 몇 번인지 출력하라.
- <입력>
- 노드의 개수 N
- 트리 상에 연결된 두 정점 (2~N)
- 가장 가까운 공통 조상을 알고 싶은 쌍의 개수 M
- 정점 쌍 (N+1+M)
- <출력>
- M개의 줄에 차례대로 입력받은 두 정점의 가장 가까운 공통 조상을 출력한다.
- 문제 재정의
- 스패닝 트리가 주어질 때 최소 공통 조상을 출력하라.
- 해결 방법
- 희소 배열로 부모 저장, LCA로 빠르게 탐색
- 실수한 점, 개선한 점
- MAX_LOG의 크기를 가진 배열에 MAX_LOG번째 값을 건드렸는데 이차원 배열로 다음 내용이 뒤집어 씌워져 런타임 에러를 내지 않고 오류를 만들어 내서 수정했다.
- dfs로 탐색할 때 이미 탐색했는지를 확인하지 않아 에러가 났다.
- LCA에서 반환할 때 x가 아닌 x의 부모값을 반환해야 한다.
- cin.sync_with_stdio(0);만 썼다가 시간 초과가 났다. 아래 코드처럼 모두 다 써야 시간 초과가 나지 않는다.
#include <iostream>
#include <vector>
#define FOR(i, k, N) for(int i=k; i<=N; i++)
using namespace std;
const int MAX_N = 100001;
//log2(100001) = 16.xxx이므로 희소 배열을 생성할 때 16까지만 만들면 된다.
const int MAX_LOG = 18;
//정점의 개수 N, 두 노드의 쌍 M
int N, M;
//트리와 노드의 깊이
vector<int> edge[MAX_N];
int diff[MAX_N];
//희소 배열
int parent[MAX_N][MAX_LOG];
void input()
{
cin >> N;
FOR(i, 1, N-1) {
int x, y;
cin >> x >> y;
//x와 y가 연결되어 있는 간선
edge[x].push_back(y);
edge[y].push_back(x);
}
}
//dfs를 통해 노드의 깊이 측정
void dfs(int Node)
{
FOR(i, 0, (int)edge[Node].size()-1) {
//만약 이미 탐색되었다면 pass
if(diff[edge[Node][i]])
continue;
//다음 노드는 깊이가 1만큼 더 크다
diff[edge[Node][i]] = diff[Node] + 1;
//다음 노드의 엄마 노드는 Node이다.
parent[edge[Node][i]][0] = Node;
dfs(edge[Node][i]);
}
}
//x와 y의 최소 공통 조상을 반환
int LCA(int x, int y)
{
//x의 깊이가 더 크도록 한다.
if(diff[x] < diff[y])
swap(x, y);
//x와 y의 깊이가 같도록 x의 깊이를 희소배열을 통해 줄여준다.
for(int i=MAX_LOG-1; i>=0; i--) {
if(diff[x] - diff[y] >= (1<<i)) {
x = parent[x][i];
}
}
if(x == y)
return x;
//x와 y의 깊이를 최소 공통 조상이 나타날때까지 동시에 감소시킨다.
for(int i=MAX_LOG-1; i>=0; i--) {
if(parent[x][i] != parent[y][i]) {
x = parent[x][i];
y = parent[y][i];
}
}
return parent[x][0];
}
void solve()
{
cin >> M;
FOR(i, 1, M) {
int x, y;
cin >> x >> y;
//x와 y의 최소 공통 조상을 출력
cout << LCA(x, y) << '\n';
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
input();
diff[1]=1;
dfs(1);
//희소 배열 생성
FOR(j, 1, MAX_LOG-1)
FOR(i, 1, N)
parent[i][j] = parent[parent[i][j-1]][j-1];
solve();
return 0;
}
※현재 고등학교 등교 중인 학생입니다. 이제 알고리즘을 본격적으로 공부하기 시작해서 아직 초보입니다. 혹시 제가 잘못 알고 있는 점이나 더 좋은 풀이 방법이 있어 댓글에 남겨주시면 감사히 하나하나 열심히 읽어보겠습니다. 좋아요, 단순한 댓글 한마디라도 저에겐 큰 힘이 됩니다! 감사합니다.
'알고리즘 > 백준' 카테고리의 다른 글
[C++] 11280번 2-SAT - 3 - 그래프 이론, 강한 연결 요소, 2-sat (0) | 2022.10.20 |
---|---|
[C++] 11437번 LCA - 트리, 최소 공통 조상 (0) | 2022.10.19 |
[C++] 14725번 개미굴 - 자료 구조, 문자열, 트리, 트라이 (0) | 2022.10.19 |
[C++] 16287번 Parcel - DP, 중간에서 만나기 (0) | 2022.10.18 |
[C++] 11689번 GCD(n, k) = 1 - 수학, 정수론, 오일러 피 함수 (0) | 2022.10.18 |
Comments