코딩/알고리즘

[알고리즘] LCA (Lowest Common Ancestor)

최선을 다하는 2022. 2. 7. 12:00

LCA 알고리즘은 트리 혹은 DAG에서 사용하는 알고리즘으로 최소 공통 조상을 구하는 알고리즘이다.

1. 각 트리의 깊이를 구한다. 루트 노드의 깊이는 0이다. 깊이를 구하면서 각 노드의 부모 노드도 저장해둔다.

     - BFS 와 DFS 등으로 구현할 수 있다.

2. 그 다음 각 노드의 2^K 승만큼의 부모 노드를 저장해둔다.

     - 모든 부모 노드를 저장할 필요가 없는 이유는 이진수가 모든 수를 표현할 수 있듯 3번째 조상을 구하려면 1번째           조상의 2번째 조상을 구하면 되기 때문이다.

3. 공통 조상을 구하는 두 노드 u v 중 더 깊은 노드를 같은 높이의 부모노드로 변경한다. 이는 비트 연산자로 구현할 수     있다. 그다음 두 노드들의 가장 윗 노드들부터 내려오면서 확인한다. 이때 두 숫자가 달라지는 시점이 공통 조상의       자식인 시점이며 이 시점의 부모 노드가 최소 공통조상이 된다.

 

아래의 코드는 SWEA 1248번 공통 조상 문제의 코드이다.

#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
#define MAX 14
int V, E,S,D;
vector <int> adj[10001];
int parent[10001][MAX+1];
int depth[10001];
int lca_node,ans;
void make_depth() {
	queue <int> q;
	int prev = 1;
	for (int next : adj[1]) {
		q.push(next);
		depth[next] = 1;
		parent[next][0] = 1;
	}
	while (!q.empty()) {
		int cur = q.front(); q.pop();
		for (int next : adj[cur]) {
			q.push(next);
			parent[next][0] = cur;
			depth[next] = depth[cur] + 1;
		}
	}
}

void connect() {
	for (int k = 1; k <= MAX; k++) {
		for (int cur = 1; cur <= V; cur++) {
			parent[cur][k] = parent[parent[cur][k - 1]][k - 1];
		}
	}
}

int LCA(int u, int v) {
	if (depth[u] < depth[v]) {
		int temp = u;	u = v;	v = temp;
	}
	int diff = depth[u] - depth[v];
	for(int i = 0; diff != 0; i++) {
		if ((diff & 1))
			u = parent[u][i];
		diff = diff >> 1;
	}
	if (u != v) {
		for (int i = MAX; i >= 0; i--) {
			if (parent[u][i] != 0 && parent[u][i] != parent[v][i]) {
				u = parent[u][i];
				v = parent[v][i];
			}
		}
		u = parent[u][0];
	}
	return u;
}

int main(int argc, char** argv)
{
	int test_case;
	int T;

	freopen("input.txt", "r", stdin);
	cin >> T;

	for (test_case = 1; test_case <= T; ++test_case)
	{
		memset(parent, 0, sizeof(parent));
		memset(depth, -1, sizeof(depth));
		cin >> V >> E >> S >> D;
		for (int i = 1; i <= V; i++)
			adj[i].clear();
		for (int i = 0; i < E; i++) {
			int a, b;
			cin >> a >> b;
			adj[a].push_back(b);
		}
		depth[1] = 0;
		make_depth();
		connect();

		cout << "#" << test_case << " "<< LCA(S, D)<< "\n";
	}
	return 0;//정상종료시 반드시 0을 리턴해야합니다.
}

'코딩 > 알고리즘' 카테고리의 다른 글

[알고리즘 - 문자열 처리] KMP  (0) 2022.02.09
[알고리즘] 위상정렬  (0) 2022.02.08
[알고리즘] LIS LCS  (2) 2022.02.05