LCA๋ Lowest Common Ancestor์ ์ฝ์๋ก ์ต์ ๊ณตํต ์กฐ์์ ์ฐพ๋ ์๊ณ ๋ฆฌ์ฆ์ด๋ค.
ํธ๋ฆฌ๊ตฌ์กฐ์์ ์ฌ์ฉ๋๋ฉฐ ๋ ์ ์ u, v์์ ๊ฐ์ฅ ๊ฐ๊น์ด ๊ณตํต ์กฐ์์ ์ฐพ์ผ๋ฉด ๋๋ค.

์ด ๊ทธ๋ํ์์ 13๋ฒ ์ ์ ๊ณผ 15๋ฒ ์ ์ ์ ์ต์๊ณตํต์กฐ์์ 5๋ฒ ์ ์ ์ด๋ค.

11๋ฒ ์ ์ ๊ณผ 13๋ฒ ์ ์ ์ ์ต์๊ณตํต์กฐ์์ 1๋ฒ ์ ์ ์ด๋ค.

์ด๋ฐ ์ํฉ์์ 12๋ฒ ์ ์ ๊ณผ 14๋ฒ ์ ์ ์ ์ต์๊ณตํต์กฐ์์ 12๋ฒ ์ ์ ์ด๋ค.
๐น๏ธ LCA ๊ณผ์
1๏ธโฃ ์ ๋ ฅ๋ฐ์ ์ ์ ๊ณผ ๊ฐ์ ์ผ๋ก ์๋ฐฉํฅ ๊ทธ๋ํ (ํธ๋ฆฌ๊ตฌ์กฐ) ๋ฅผ ์์ฑํ๋ค.
2๏ธโฃ BFS๋ DFS๋ฅผ ์ฌ์ฉํ์ฌ depth๋ฅผ, DP๋ฅผ ํ์ฉํ์ฌ parent ์ ๋ณด๋ฅผ ์ ์ฅํ๋ ํธ๋ฆฌ๋ฅผ ์์ฑํ๋ค.
3๏ธโฃ LCA(u, v) ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ u์ v์ ์ต์๊ณตํต์กฐ์์ ์ฐพ๋๋ค.
- u์ v ์ค์์ ๊น์ด๊ฐ ๋ ๊น์ ์ ์ ์ ์์ ๊น์ด์ ์ ์ ๊น์ง ๊ฑฐ์ฌ๋ฌ ์ฌ๋ผ๊ฐ๋ค. (๊น์ด ๋์ผํ๊ฒ ๋ง๋ค์ด์ฃผ๊ธฐ)
- ์ ์ ์ ๋ถ๋ชจ๋ฅผ ๋ฐ๋ผ ๊ฐ์ด ํ๋์ฉ ์ฌ๋ผ๊ฐ์ LCA๋ฅผ ์ฐพ๋๋ค.
โก๏ธ ์ด ๋ฐฉ๋ฒ์ผ๋ก ์งํํ ์ ํธ๋ฆฌ ๊น์ด๊ฐ ๊ทธ๋ค์ง ๊น์ง ์์ผ๋ฉด ๊ด์ฐฎ์ผ๋, ๊น์ด๊ฐ ๊น์ด์ง์๋ก ์๊ฐ๋ณต์ก๋๊ฐ ์๋นํ ์ปค์ง๋ค.
โณ ์๊ฐ๋ณต์ก๋ : O(H) (ํธ๋ฆฌ์ ๊น์ด = H, ์ต์ ์ ๊ฒฝ์ฐ O(N))
๐ ์์ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ๊ฐ ๋ ธ๋๊ฐ ๊ฑฐ์ฌ๋ฌ ์ฌ๋ผ๊ฐ๋ ์๋๋ฅผ ๋น ๋ฅด๊ฒ ๋ง๋ค์ด ์ฃผ๋ ๊ฒ์ด๋ค.
๋ง์ฝ, 15์นธ์ ์ด๋ํ๋ค๋ฉด 8์นธ → 4์นธ → 2์นธ → 1์นธ ์ด๋ฐ์์ผ๋ก ์ด๋ํ ์ ๋ณด๋ค ๋น ๋ฅด๊ฒ LCA๋ฅผ ๊ตฌํ ์ ์๋ค.
์ฆ, 2์ ์ ๊ณฑ์ ํํ๋ก ๊ฑฐ์ฌ๋ฌ ์ฌ๋ผ๊ฐ๋ฉด O(logN)์ ์๊ฐ๋ณต์ก๋๋ฅผ ๊ฐ์ง๊ฒ ๋๋ค.
LCA ์ฝ๋ ๊ตฌํ
1๏ธโฃ ์ ๋ ฅ๋ฐ์ ์ ์ ๊ณผ ๊ฐ์ ์ผ๋ก ์๋ฐฉํฅ ๊ทธ๋ํ๋ฅผ ์์ฑํ๋ ์ฝ๋
static ArrayList<Integer>[] tree;
public static void main(String[] args) throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(br.readLine());
// ๊ทธ๋ํ ์์ฑ ๋ฐ ์ด๊ธฐํ
tree = new ArrayList[n+1];
for(int i=0; i<=n; i++){
tree[i] = new ArrayList<>();
}
StringTokenizer st = new StringTokenizer(br.readLine());
for(int i=1; i<n; i++){
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
tree[a].add(b); // ํธ๋ฆฌ ์ ๋ณด ์ ์ฅ → ๋
ธ๋ ์ฐ๊ฒฐ
tree[b].add(a);
}
}
2๏ธโฃ BFS๋ DFS๋ฅผ ์ด์ฉํ์ฌ ๊น์ด ์ ๋ณด ์ ์ฅ
static int[] depth;
static int[][] parent;
// BFS
static void bfs(int start) {
ArrayDeque<Integer> dq = new ArrayDeque<>();
depth[start] = 1;
dq.add(start);
while (!dq.isEmpty()) {
int now = dq.poll();
for (int next : tree[now]) {
if (depth[next] == 0) {
depth[next] = depth[now]+1;
parent[0][next] = now; // ํด๋น ๋
ธ๋ ๋ฐ๋ก ์์ ์๋ ๋ถ๋ชจ ์ ์ฅ. (next ๊น์ด = now ๊น์ด + 1) next์ ๋ถ๋ชจ = now
dq.add(next);
}
}
}
}
// DFS
static void dfs(int node, int cnt) {
// 1. depth ๊ธฐ๋ก
depth[node]= cnt;
// 2. ์์๋ค์ depth ๊ธฐ๋ก
int len= tree[node].size(); // ์์ ๊ฐ์
for(int i=0; i<len; i++) {
int next= (Integer)tree[node].get(i);
//์์ง ๊น์ด๋ฅผ ๋ชจ๋ฅด๋ฉด → ๋ฏธ๋ฐฉ๋ฌธ ๋
ธ๋
if(depth[next]==0) {
dfs(next, cnt+1);
parent[0][next]= node; //next ๋
ธ๋์ 2^0 = ์ฒซ๋ฒ์งธ ๋ถ๋ชจ ๊ธฐ๋ก → ํด๋น ๋
ธ๋ ๋ฐ๋ก ์์ ์๋ ๋ถ๋ชจ ์ ์ฅ
}
}
}
3๏ธโฃ DP๋ฅผ ํ์ฉํ์ฌ ๋ถ๋ชจ ์ฑ์ฐ๊ธฐ
parent[K][V] = parent[K-1][parent[K-1][V]] โก๏ธ ์ ์ V์ 2^K๋ฒ์งธ ์กฐ์ ์ ์ ๋ฒํธ
static void getSparseTable(){
for(int i=1; i<=logN; i++){
for(int j=1; j<=n; j++){
parent[i][j] = parent[i-1][parent[i-1][j]];
}
}
}
ex)
1, 2, 4๋ 2^0, 2^1, 2^2์ ๊ฐ์ด๋ค. i์ ๊ฐ์ด 2์ ์ง์ ์ชฝ์ ์๋ฏธํ๋ค. (i=0, 1, 2, .... 17)
parent[2][1] = parent[1][0] = 0 โ ๋ ธ๋ 1์์ 2๋งํผ ์ฌ๋ผ๊ฐ ๋ ธ๋์ ๊ฐ = 0
parent[2][2] = parent[1][1] = 0 โ ๋ ธ๋ 2์์ 2๋งํผ ์ฌ๋ผ๊ฐ ๋ ธ๋์ ๊ฐ = 0
parent[2][3] = parent[1][1] = 0 โ ๋ ธ๋ 3์์ 2๋งํผ ์ฌ๋ผ๊ฐ ๋ ธ๋์ ๊ฐ = 0
parent[2][4] = parent[1][2] = 1 โ ๋ ธ๋ 4์์ 2๋งํผ ์ฌ๋ผ๊ฐ ๋ ธ๋์ ๊ฐ = 1
parent[2][8] = parent[1][3] = 1 โ ๋ ธ๋ 8์์ 2๋งํผ ์ฌ๋ผ๊ฐ ๋ ธ๋์ ๊ฐ = 1
parent[2][15] = parent[1][11] = 1 โ ๋ ธ๋ 15์์ 2๋งํผ ์ฌ๋ผ๊ฐ ๋ ธ๋์ ๊ฐ = 5
4๏ธโฃ LCA ์ฐพ๊ธฐ
static int getLCA(int a, int b) {
if (depth[a] < depth[b]) {
return getLCA(b,a);
}
// ๋์ด ๋ง์ถ๊ธฐ
for(int i=0; i<=logN; i++){
if(((depth[a]-depth[b]) & (1<<i)) >=1){ // ๋นํธ AND ์ฐ์ฐ => ๋๋ค 1์ผ ๊ฒฝ์ฐ 1๋ก ๋ฐํ
a = parent[i][a];
}
}
// ๋์ด ๋ง์ถ๊ธฐ -> ๋ค๋ฅธ ๋ฐฉ๋ฒ
/*for(int i=logN; i>=0; i--){
if(Math.pow(2,i)<=depth[a]-depth[b])){
a = parent[i][a];
}
}*/
// ๋์ด ๋ง์ท์ผ๋ฉด ๊ฐ์์ง ๊ฒ์ฌ
if(a==b){
return a;
}
// ๊ณตํต์กฐ์์ด ์๋ ๋๊น์ง ๋ถ๋ชจ๋ฅผ ๋ฐ๋ผ ์ฌ๋ผ๊ฐ
// ์ต์ข
์ ์ผ๋ก๋ LCA ๋ฐ๋ก ๋ฐ์นธ๊น์ง๋ง ์ฌ๋ผ๊ฐ.
for(int i=logN; i>=0; i--){
if (parent[i][a] != parent[i][b]) {
a = parent[i][a];
b = parent[i][b];
}
}
return parent[0][a];
}
/**
* 11438_LCA 2
* sparse table ์ต์ ๊ณตํต์กฐ์ ๋น ๋ฅด๊ฒ ๊ตฌํ๊ธฐ
* N์ ํฌ๊ธฐ = 10๋ง
* M์ ํฌ๊ธฐ = 10๋ง => 2^17=์ฝ 13๋ง => sparseTable ์ฌ์ฉ
*/
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.StringTokenizer;
public class BOJ_11438_LCA2 {
static int n,m;
static int logN = 17; // K => 2์ ๋ช ์ ๊ณฑ๊น์ง ๊ณ์ฐํ ์ง ์ ์ฅํ ๋ณ์ => 17๋ก ์ ์ธํด๋ ๋ฌด๋ฐฉ
static ArrayList<Integer>[] tree;
static int[] depth;
static boolean[] visited;
static int[][] parent; // sparse table
static void bfs(int start) {
ArrayDeque<Integer> dq = new ArrayDeque<>();
depth[start] = 1;
dq.add(start);
while (!dq.isEmpty()) {
int now = dq.poll();
for (int next : tree[now]) {
if (depth[next] == 0) {
depth[next] = depth[now]+1;
parent[0][next] = now;
dq.add(next);
}
}
}
}
static void getSparseTable(){
for(int i=1; i<=logN; i++){
for(int j=1; j<=n; j++){
parent[i][j] = parent[i-1][parent[i-1][j]];
}
}
}
static int getLCA(int a, int b) {
if (depth[a] < depth[b]) {
return getLCA(b,a);
}
// ๋์ด ๋ง์ถ๊ธฐ
for(int i=0; i<=logN; i++){
if(((depth[a]-depth[b]) & (1<<i)) >=1){ // ๋นํธ AND ์ฐ์ฐ => ๋๋ค 1์ผ ๊ฒฝ์ฐ 1๋ก ๋ฐํ
a = parent[i][a];
}
}
// ๋์ด ๋ง์ถ๊ธฐ -> ๋ค๋ฅธ ๋ฐฉ๋ฒ
/*for(int i=logN; i>=0; i--){
if(Math.pow(2,i)<=(depth[a]-depth[b])){
a = parent[i][a];
}
}*/
// ๋์ด ๋ง์ท์ผ๋ฉด ๊ฐ์์ง ๊ฒ์ฌ
if(a==b){
return a;
}
// ๊ณตํต์กฐ์์ด ์๋ ๋๊น์ง ๋ถ๋ชจ๋ฅผ ๋ฐ๋ผ ์ฌ๋ผ๊ฐ
// ์ต์ข
์ ์ผ๋ก๋ LCA ๋ฐ๋ก ๋ฐ์นธ๊น์ง๋ง ์ฌ๋ผ๊ฐ.
for(int i=logN; i>=0; i--){
if (parent[i][a] != parent[i][b]) {
a = parent[i][a];
b = parent[i][b];
}
}
return parent[0][a];
}
public static void main(String[] args) throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
n = Integer.parseInt(br.readLine());
tree = new ArrayList[n+1];
visited = new boolean[n + 1];
depth = new int[n + 1];
parent = new int[logN + 1][n+1]; // ์๊ฐ๋ณต์ก๋์ ์ฒซ๋ฒ์งธ ๋ฐฐ์ด์ ์ ์ ์ ๋ฃ๋ ๊ฒ์ด ๋ ํจ์จ์
for(int i=0; i<=n; i++){
tree[i] = new ArrayList<>();
}
StringTokenizer st = new StringTokenizer(br.readLine());
for(int i=1; i<n; i++){
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
tree[a].add(b);
tree[b].add(a);
}
bfs(1);
getSparseTable();
m= Integer.parseInt(br.readLine());
for(int i=1; i<=m; i++){
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
bw.write(getLCA(a, b) + "\n");
}
bw.flush();
bw.close();
}
}
'์๊ณ ๋ฆฌ์ฆ > ๐๏ธ ์ ๋ฆฌ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
MST(์ต์์ ์ฅํธ๋ฆฌ) - ํฌ๋ฃจ์ค์นผ & ํ๋ฆผ ์๊ณ ๋ฆฌ์ฆ (1) | 2023.10.23 |
---|---|
Bellman-Ford (๋ฒจ๋ง-ํฌ๋) (0) | 2023.09.08 |
Binary Search (์ด๋ถํ์) (0) | 2023.09.01 |
Floyd-Warshall (ํ๋ก์ด๋ ์์ ) (0) | 2023.08.29 |
Union Find (์ ๋์จ ํ์ธ๋) (0) | 2023.08.29 |