[자료ꡬ쑰] λ””μŠ€μ‘°μΈνŠΈ-μ…‹(Disjoint-Set) - μœ λ‹ˆμ˜¨ νŒŒμΈλ“œ

2020. 12. 24. 13:09γ†πŸ‘¨πŸ»‍🏫 ν”„λ‘œκ·Έλž˜λ°/자료ꡬ쑰

ν•΄λ‹Ή 글은 λ§ˆμ΄κ΅¬λ―Έλ‹˜μ˜ κΈ€κ³Ό μœ„ν‚€λ°±κ³Όλ₯Ό μ°Έκ³ ν•˜λ©° 슀슀둜 κ³΅λΆ€ν•œ λ‚΄μš©μ„ μ •λ¦¬ν•œ κ²ƒμž…λ‹ˆλ‹€.

Disjoint-Set을 같이 κ³΅λΆ€ν•΄λ΄…μ‹œλ‹€!

Disjoint(μ„œλ‘œμ†Œ) Set(집합)λŠ” λ§Žμ€ μ„œλ‘œμ†Œ λΆ€λΆ„μ§‘ν•©λ“€λ‘œ λ‚˜λˆ μ§„ μ›μ†Œλ“€μ— λŒ€ν•œ 정보λ₯Ό μ €μž₯ν•˜κ³  μ‘°μž‘ν•˜λŠ” μžλ£Œκ΅¬μ‘°μž…λ‹ˆλ‹€.

μ΄λŸ¬ν•œ νŠΉμ§•μœΌλ‘œ 인해 Disjoint Set은 νŒŒν‹°μ…˜(partitioning) λ¬Έμ œμ— 주둜 μ΄μš©λ©λ‹ˆλ‹€. 

Disjoint Set μžλ£Œκ΅¬μ‘°μ—λŠ” 3κ°€μ§€μ˜ μ€‘μš”ν•œ 연산이 μ‘΄μž¬ν•©λ‹ˆλ‹€.

μ„œλ‘œμ†Œ μ§‘ν•©μ΄λž€? κ³΅ν†΅ μ›μ†Œκ°€ μ—†λŠ” 두 μ§‘ν•©, ꡐ집합이 곡집합인 μ§‘ν•©μž…λ‹ˆλ‹€.


μ—°μ‚°

  1. 메이크 μ…‹ (MakeSet): 말 κ·ΈλŒ€λ‘œ νŠΉμ • ν•œ μ›μ†Œλ§Œμ„ κ°€μ§€λŠ” 집합(λ“€)을 λ§Œλ“œλŠ” κ²ƒμž…λ‹ˆλ‹€.

  2. νŒŒμΈλ“œ (Find): μ–΄λ–€ μ›μ†Œκ°€ μ£Όμ–΄μ‘Œμ„ λ•Œ 이 μ›μ†Œκ°€ μ†ν•œ 집합을 λ°˜ν™˜ ν•©λ‹ˆλ‹€. 일반적으둜 ν•΄λ‹Ή μ›μ†Œκ°€ μ†ν•œ 집합을 λŒ€ν‘œν•˜λŠ” μ›μ†Œλ₯Ό λ°˜ν™˜ ν•©λ‹ˆλ‹€.

  3. μœ λ‹ˆμ˜¨ (Union): 두 개의 집합을 ν•˜λ‚˜μ˜ μ§‘ν•©μœΌλ‘œ ν•©μΉ˜λŠ” μ—°μ‚°μž…λ‹ˆλ‹€. 이 λ°©λ²•μ—μ„œ μœ λ‹ˆμ˜¨ 연산은 두 λŒ€ν‘œ μ›μ†Œλ₯Ό μž…λ ₯ 인자둜 μ‚¬μš©ν•©λ‹ˆλ‹€.

β€» λŒ€ν‘œ μ›μ†Œ: μ‚¬μš©λ˜λŠ” 연산듀을 보면 νŠΉμ • μ›μ†Œκ°€ μ†ν•œ 집합을 λŒ€ν‘œν•˜λŠ” λŒ€ν‘œ μ›μ†Œκ°€ μ“°μž…λ‹ˆλ‹€. κ·ΈλŸ¬λ‹ˆ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° 전에 λŒ€ν‘œλ₯Ό μ •ν•  기쀀을 μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€. (숫자면 κ°€μž₯ μž‘μ€ 수, 문자면 μ‚¬μ „μˆœ κ°€μž₯ μ•žμ˜ 단어 λ“±)


κ΅¬ν˜„

λ””μŠ€μ‘°μΈνŠΈ μ…‹ μžλ£Œκ΅¬μ‘°λŠ” 일반적으둜 트리 ꡬ쑰둜 κ΅¬ν˜„λ©λ‹ˆλ‹€. ν•˜λ‚˜μ˜ 집합은 ν•˜λ‚˜μ˜ 트리둜 κ΅¬ν˜„λ˜λ©° 트리의 λ£¨νŠΈλ…Έλ“œκ°€ ν•΄λ‹Ή μ§‘ν•©μ˜ λŒ€ν‘œμ›μ†Œκ°€ λ˜λŠ” κ²ƒμž…λ‹ˆλ‹€. μ΄λ•Œ 경둜 μ••μΆ•(path compression)μ΄λΌλŠ” 방법을 μ‚¬μš©ν•˜μ—¬ μ—°μ‚° 속도λ₯Ό ν–₯μƒμ‹œμΌœ νŠΉμ • μ›μ†Œμ—μ„œ ν•΄λ‹Ή μ›μ†Œκ°€ μ†ν•œ μ§‘ν•©μ˜ λŒ€ν‘œμ›μ†Œμ— λ°”λ‘œ μ ‘κ·Ό ν•  수 μžˆκ²Œν•©λ‹ˆλ‹€.

κ²½λ‘œμ••μΆ•(path compression)은 νŒŒμΈλ“œ 연산을 μˆ˜ν–‰ ν•  λ•Œλ§ˆλ‹€ 루트 λ…Έλ“œκΉŒμ§€ μˆœνšŒμ€‘ λ°©λ¬Έν•œ 각 λ…Έλ“œλ“€μ΄ 직접 루트 λ…Έλ“œλ₯Ό 가리킀도둝 κ°±μ‹ ν•©λ‹ˆλ‹€. μˆœνšŒλŠ” μž¬κ·€ ν•¨μˆ˜λ₯Ό 톡해 μ§„ν–‰λ©λ‹ˆλ‹€.

β€» λ°°μ—΄ parents : parents[x] = y; λΌλŠ” λ¬Έμž₯μ—μ„œ νŠΉμ • μ›μ†Œ xκ°€ μ†ν•œ μ§‘ν•©μ˜ λŒ€ν‘œ μ›μ†Œκ°€ yλΌλŠ” 것을 μ˜λ―Έν•©λ‹ˆλ‹€.

메이크 μ…‹ μ—°μ‚° (MakeSet)

void make_set(int x){
	parents[x] = x;    // 자기 μžμ‹ λ§Œμ„ μ›μ†Œλ‘œ κ°€μ§€λŠ” 집합을 생성
	return;
}

νŒŒμΈλ“œ μ—°μ‚° (Find)

int do_find(int x) {

	if(x == parents[x]) return x;   //ν•΄λ‹Ή μ§‘ν•©μ˜ λŒ€ν‘œ μ›μ†Œκ°€ 자기 μžμ‹ μΌ 경우
   
	return parents[x] = do_find(parents[x]); //경둜 μ••μΆ•: μˆœνšŒν•˜λ©° κ±°μ²˜κ°„ μ›μ†Œλ“€μ„ λ°”λ‘œ 루트둜 μ—°κ²°
    
}

μœ λ‹ˆμ˜¨ μ—°μ‚° (Union)      

void do_union(int u,int v){

	u = do_find(u);   //μ›μ†Œ uκ°€ μ†ν•œ μ§‘ν•©μ˜ 루트 λ…Έλ“œ-> u에 λ‹€μ‹œ λŒ€μž….
	v = do_find(v);   //μ›μ†Œ vκ°€ μ†ν•œ μ§‘ν•©μ˜ 루트 λ…Έλ“œ-> v에 λ‹€μ‹œ λŒ€μž….
    
	if(u == v) return; // λ£¨νŠΈλ…Έλ“œκ°€ κ°™λ‹€λ©΄ 이미 같은 μ§‘ν•©μ΄λΌλŠ” 의미 μ΄λ―€λ‘œ 리턴
    
    	parents[u] = v; // 그렇지 μ•Šλ‹€λ©΄ v의 λ£¨νŠΈλ…Έλ“œμ— u의 루트 λ…Έλ“œλ₯Ό λΆ™νžŒλ‹€.
    
	return;
}

μ—°μŠ΅

같이 문제λ₯Ό ν’€λ©΄μ„œ Disjoint-Set와 더 κ°€κΉŒμ›Œμ Έλ΄…μ‹œλ‹€! 

[BOJ 1976번: μ—¬ν–‰κ°€μž] https://www.acmicpc.net/problem/1976

#include<bits/stdc++.h>
using namespace std;
const int MAX=200+2;

bool impossible=false;
int N,M,parents[MAX]; // parents[x]=y μ›μ†Œ xκ°€ μ†ν•œ μ§‘ν•©μ˜ λŒ€ν‘œ μ›μ†ŒλŠ” yμž„μ„ 의미
vector<int> plan;

int do_find(int u){
	if(u==parents[u]) return u;
	return parents[u]=do_find(parents[u]); 
}

void do_union(int u,int v){  
	u=do_find(u);
	v=do_find(v);
	if(u==v) return;
	parents[u] = v;
}

int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cin>>N>>M;
   
	for(int i=1;i<=N;i++) parents[i]=i; //MakeSet 연산을 for문으둜 λŒ€μ‹ 
    
	for(int i=1;i<=N;i++){
		for(int j=1;j<=N;j++){
			int connection;
			cin>>connection; 
			if(connection){  
				do_union(min(i,j),max(i,j));
                
                //μ—°κ²° λ˜μ–΄μžˆμ„ 경우 Union μ—°μ‚°, 큰 μˆ˜κ°€ λ£¨νŠΈλ…Έλ“œκ°€ 되게 κΈ°μ€€ μ„€μ • 
			}
		}
	}

	while(M--){
		int city;
		cin>>city;
		plan.push_back(city);
	}
    
	//μ—¬ν–‰ κ³„νš 쀑 ν˜„μž¬ λ„μ‹œμ™€ λ‹€μŒ λ„μ‹œκ°€ 같은 집합인지 확인
   	 //( λŒ€ν‘œ μ›μ†Œκ°€ κ°™λ‹€ == 같은 집합이닀 == μ—°κ²°λ˜μ–΄μžˆλ‹€ )
     
	for(int i=0;i<(int)plan.size()-1;i++){
		if(do_find(plan[i])!=do_find(plan[i+1])) impossible=true;
	}

	if(impossible) cout<<"NO\n";
	else cout<<"YES\n";

	return 0;
}

λΆ€μ‘±ν•œ 뢀뢄이 있으면 λŒ“κΈ€ λΆ€νƒλ“œλ¦¬κ² μŠ΅λ‹ˆλ‹€!