집합의 표현(Union-Find)

2023. 1. 2. 13:48C++/알고리즘

https://www.acmicpc.net/problem/1717

 

1717번: 집합의 표현

첫째 줄에 n(1 ≤ n ≤ 1,000,000), m(1 ≤ m ≤ 100,000)이 주어진다. m은 입력으로 주어지는 연산의 개수이다. 다음 m개의 줄에는 각각의 연산이 주어진다. 합집합은 0 a b의 형태로 입력이 주어진다. 이는

www.acmicpc.net

 

분리 집합(Disjoint Set): 교집합이 존재하지 않는 두 개 이상의 집합. 구분해야 하는 데이터 집합을 다룰때 사용함.

 

분리 집합은 보통 Union-Find 알고리즘을 사용하여 트리 구조로 구현한다.

 

Union-Find 알고리즘

1. make_set(x) -> x원소를 유일하게 갖고있는 집합을 생성.

2. union(x, y) -> x가 속한 집합과 y가 속한 집합을 합친다.

3. find(x) -> x가 속한 집합을 찾는다.

 

Union-Find 알고리즘(Tree)

1. make_set(x) -> 각 원소를 전부 루트노드로 만든다. 루트노드 = 자기자신.

2. union(x, y) -> x가 속한 트리와 y가 속한 트리의 루트노드를 찾는다(find). 루트 노드가 다르다면 x를 y의 부모노드로 만들어서 트리를 합친다.

3. find(x) -> x가 속한 트리의 루트노드를 찾는다.

 

집합의 표현 문제는 숫자의 범위가 '0~1000000'으로 주어졌다. 단순히 Union-Find 알고리즘을 사용하여 트리를 구성하게 된다면 최악의 경우에 find마다 O(N-1)의 시간 복잡도가 발생할 수 있어서 시간초과가 발생한다.

find 함수에서 재귀함수 호출을 통한 트리의 모든 노드의 부모노드를 루트노드로 만들어주어서 경로의 압축을 구현해주어야 한다.

 

#include <iostream>
using namespace std;

int parent[1000001];

int find_parent(int index)
{
	// 재귀호출을 통한 부모노드 = 루트노드화로 경로압축
	if (parent[index] == index) return index;
	else parent[index] = find_parent(parent[index]);
	return parent[index];
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	int n, m;
	cin >> n >> m;

	for (int i = 0; i <= n; i++)
	{
		parent[i] = i;
	}

	while (m--)
	{
		int cmd, n1, n2;
		cin >> cmd >> n1 >> n2;
		if (n1 > n2)
		{
			int tmp = n1;
			n1 = n2;
			n2 = tmp;
		}
		int pn1 = find_parent(n1);
		int pn2 = find_parent(n2);
		switch (cmd)
		{
		case 0:
			if (pn1 != pn2) parent[pn2] = pn1;
			break;
		case 1:
			if (pn1 == pn2) cout << "YES";
			else cout << "NO";
			cout << '\n';
			break;
		default:
			break;
		}
	}
}

'C++ > 알고리즘' 카테고리의 다른 글

구간 나누기(DP)  (0) 2023.05.04
고층빌딩(DP)  (1) 2023.05.02
외판원 순회(DP, BitMask)  (0) 2022.12.22
연료 채우기(Greedy)  (0) 2022.12.20
줄세우기(DP)  (0) 2022.12.14