[백준 11438 : JAVA] LCA 2 / 최소 공통 조상
문제

풀이
LCA(Lowest Common Acestor_최소 공통 조상) 알고리즘을 사용하는 기본 유형의 문제다.
해당 문제에서 주의깊게 봐둬야할 건 각 노드의 2^i 번 째 조상노드를 테이블에 저장하기 위해 DP를 사용한 점화식이다.
- DFS 탐색을 통해 각 노드별 depth를 구해 배열에 저장하고, 각 노드의 첫 번째 조상노드를 parent 배열에 담는다. (DP 사전준비)
- DP 알고리즘을 통해 각 노드의 모든 2^i 번 째 조상노드를 구해 parent 배열에 저장한다.
- LCA(a, b)에서 a와 b의 깊이를 맞춘 후, 차례로 2^i 번 째 조상노드를 구해가며 최소 공통 조상을 찾는다.
위의 2번을 보면 DP를 통해 각 노드의 2^i 번 째 조상노드를 저장한다고 되어 있는데, 해당 코드를 보자.
for (int i = 1; i < depth; i++) { for (int j = 1; j <= N; j++) { parent[j][i] = parent[ parent[j][i - 1] ][i - 1]; } }
A 정점의 2^1 번 째 조상은 A 정점의 2^0 번 째 조상노드의 2^0 번 째 조상과 같다.
A 정점의 2^1 번 째 조상노드 = A 정점의 2^0 번 째 조상노드의 2^0 번 째 조상노드
코드
import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; public class Main { private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); private static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); private static int N, M, K; private static List<List<Integer>> tree; private static int[] depth; private static int[][] parents; public static void main(String[] args) throws IOException { StringTokenizer st; N = Integer.parseInt(br.readLine()); tree = new ArrayList<>(); for (int i = 0; i < N + 1; i++) tree.add(new ArrayList<>()); for (int i = 0; i < N - 1; i++) { st = new StringTokenizer(br.readLine()); int a = Integer.parseInt(st.nextToken()); int b = Integer.parseInt(st.nextToken()); tree.get(a).add(b); tree.get(b).add(a); } int tmp = 1; K = 0; while (tmp <= N) { // 최대 depth 알아내기. tmp <<= 1; K++; } depth = new int[N + 1]; parents = new int[N + 1][K]; dfs(1, 1); fillParents(); M = Integer.parseInt(br.readLine()); for (int i = 0; i < M; i++) { st = new StringTokenizer(br.readLine()); int a = Integer.parseInt(st.nextToken()); int b = Integer.parseInt(st.nextToken()); int lca = lca(a, b); bw.write(lca + "\n"); } bw.flush(); bw.close(); br.close(); } private static int lca(int a, int b) { if (depth[a] < depth[b]) { // 깊이가 낮은 쪽을 기준으로 맞춘다. int temp = a; a = b; b = temp; } //더 깊은 a를 2승씩 점프하며 두 노드의 depth를 맞춘 후, 맞춘 depth의 조상 노드로 대체한다. for (int i = K - 1; i >= 0; i--) { if (Math.pow(2, i) <= depth[a] - depth[b]) { a = parents[a][i]; // a를 2^i 번 째 조상 노드로 대체한다. } } // depth 맞춘 후 대체한 조상 노드가 b와 같다면. 즉, a의 조상노드가 b라면 종료한다. if (a == b) return a; // 이제 두 노드는 같은 depth를 가지기 때문에 // 같이 2승씩 점프시키며 공통부모 바로 아래까지 올린다. for (int i = K - 1; i >= 0; i--) { if (parents[a][i] != parents[b][i]) { a = parents[a][i]; b = parents[b][i]; } } return parents[a][0]; } private static void fillParents() { for (int i = 1; i < K; i++) { // DP를 이용해 각 노드별 2^K 번 째 조상 노드를 저장한다. for (int j = 1; j <= N; j++) { parents[j][i] = parents[ parents[j][i - 1] ][i - 1]; } } } private static void dfs(int node, int cnt) { depth[node] = cnt; for (Integer next : tree.get(node)) { if (depth[next] == 0) { dfs(next, cnt + 1); parents[next][0] = node; } } } }
'Algorithm > Baekjoon' 카테고리의 다른 글
[백준 17141 : JAVA] 연구소 2 / BFS + 조합 (0) | 2020.09.27 |
---|---|
[백준 9466 : JAVA] 텀 프로젝트 / DFS (0) | 2020.09.23 |
[백준 13418 : JAVA] 학교 탐방하기 / MST (0) | 2020.09.09 |
[백준 2206 : JAVA] 벽 부수고 이동하기 / BFS (0) | 2020.09.08 |
[백준 9370 : JAVA] 미확인 도착지 / 다익스트라 (0) | 2020.08.28 |
댓글
이 글 공유하기
다른 글
-
[백준 17141 : JAVA] 연구소 2 / BFS + 조합
[백준 17141 : JAVA] 연구소 2 / BFS + 조합
2020.09.27문제 풀이 먼저 바이러스를 놓을 수 있는 k개의 위치 중 m개의 위치를 선정해야 한다. 이는 조합 ${}_k \mathrm{ C }_m$으로 나타낼 수 있다. 조합 알고리즘을 통해 m개의 시작 지점을 정한 후에는 BFS 탐색을 통해 m개의 시작 지점에 대한 탐색을 진행한다. 만약, 해당 조합의 시작 지점이 이전 조합의 시작 지점보다 탐색 시간이 길다면 해당 조합은 패스한다. 코드 import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.*; public class Main { private static BufferedReader br = new BufferedReader(new InputStreamReader(Sys… -
[백준 9466 : JAVA] 텀 프로젝트 / DFS
[백준 9466 : JAVA] 텀 프로젝트 / DFS
2020.09.23문제 풀이 이 문제는 모든 정점은 한 번씩만 탐색되어야 한다는게 핵심이다. 첫 번째 예제를 살펴보자. 가장 먼저 1번 노드부터 탐색을 진행한다. 1번 노드부터 탐색을 시작했더니 3번 노드가 사이클을 형성한다는걸 찾았다. 이게 이 문제의 포인트인데, 사이클 형성 여부를 찾기 위해 가장 쉽게 생각해볼 수 있는 방법은 다음과 같다. 처음 탐색을 시작한 노드 == 마지막으로 탐색할 노드 위의 방법으로 탐색을 진행하게 된다면 중복 탐색이 많이 발생하게 된다는 단점이 있다. 1번 노드가 사이클이 형성되지 않는단 걸 알지만, 다음 2번 노드의 탐색을 진행할 때 2 - 1 -3 으로 1번과 똑같은 탐색을 진행하게 된다. 다음 그림을 보자. 조건을 아래와 같이 바꾼다. 다음 노드가 탐색 시작 노드와 같은지 -------… -
[백준 13418 : JAVA] 학교 탐방하기 / MST
[백준 13418 : JAVA] 학교 탐방하기 / MST
2020.09.09문제 풀이 기본 형태의 MST 문제다. 다만, 최소뿐만 아니라 최대 스패닝 트리도 구해야한다는 조건이 추가되었다. 오름차순 우선순위 큐와 내림차순 우선순위 큐 2개에 간선 정보를 담아 해결했다. 코드 import java.io.*; import java.util.*; public class Main { private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); private static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); private static int N, M; private static Queue as… -
[백준 2206 : JAVA] 벽 부수고 이동하기 / BFS
[백준 2206 : JAVA] 벽 부수고 이동하기 / BFS
2020.09.08문제 풀이 2차원 평면이 주어질 때, 출발지에서 목적지까지의 단순 거리를 BFS 탐색을 통해 알아내는 문제다. 가장 기본적인 BFS 문제 유형에서 1개의 벽을 부술 수 있다는 한 가지 조건이 추가된 문제다. 추가된 위 조건으로 인해 생긴 고려사항은 다음과 같다. 기존에 (x, y) 좌표와 거리 값을 담는 distance 변수 외에 해당 좌표로 오기까지 벽을 부순 횟수를 저장할 변수가 하나 더 필요하다. 탐색할 다음 좌표가 벽이더라도 지금까지 벽을 부순 횟수가 0이라면 벽을 부수고 지날 수 있다. 기존에는 visited 변수를 통해 해당 좌표를 방문했는지만 체크했지만, 여기선 추가로 방문 여부 + 벽을 부순 횟수를 체크해야한다. 따라서, visited 변수를 boolean 타입이 아닌 int 타입으로 선언…
댓글을 사용할 수 없습니다.