문제 링크


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

 

4195번: 친구 네트워크

문제 민혁이는 소셜 네트워크 사이트에서 친구를 만드는 것을 좋아하는 친구이다. 우표를 모으는 취미가 있듯이, 민혁이는 소셜 네트워크 사이트에서 친구를 모으는 것이 취미이다. 어떤 사이

www.acmicpc.net

접근 방법


 문제에서 요구하는 것을 간단히 정리하자면, A라는 인물과 B라는 인물이 친구 관계를 맺었을 때 A의 친구 수와 B의 친구 수를 더하면 된다. 문제를 읽었을 때 두 집합이 연결(친구 관계) 또는 합(친구의 수)하여 진다는 점에 있어서 유니온 파인드 알고리즘이라는 느낌이 확실히 와 닿는다.

 

 유니온 파인드는 크게 자신의 루트를 찾는 find함수와, 두 집한간의 연결을 하는 Union함수를 사용한다.

혹여나 두 함수를 이해하지 못했다면 기본적인 UnionFind 알고리즘을 다시 공부하고 풀어보자.

 

 사실 UnionFind의 구현은 어렵지 않으나, 이 문제는 노드 번호를 이름(문자열)으로 주어지기 때문에 조금 까다롭게 느껴질 수 있다.

 

1. 문자열을 노드 번호로 정리해 보자

map <string, int> name;

name을 map의 자료형으로 위와 같이 정의했을 때, 아래와 같이 처리할 수 있다.

int idx = 1;
for (int j = 0; j < F; j++) {
	string name1, name2;
	cin >> name1 >> name2;
	if (name.count(name1) == 0) 
		name[name1] = idx++;
	if (name.count(name2) == 0) 
		name[name2] = idx++;
	Union(name[name1], name[name2]);
}

문제를 쉽게 해결하기 위해 이름(문자열)에 대해 번호를 매겨주자. 

 지금까지 입력받은 적이 없는 이름이라면 ( if (name.count(name1)==0) ), 번호를 부여한다( name[name1] = idx++) .

즉 idx는 이름에 대한 번호이며, 입력을 받은적이 있는 이름이라면 이미 번호가 매겨져 있다는 뜻으로 무시해도 된다는 것이다.

 

 예를 들면

Fred, Bob -> 1, 2

Jason, Bob -> 3, 2

이렇게 처리가 된다. 

 

이제 어려울 것이 없다. 친구 수는 (cnt[i] = 친구수 (i는 노드 번호) )라는 배열에 저장하며 처리를 할 것이며 Union함수에서 name1과 name2의 친구 관계를 형성함과 동시에 친구수를 더할 것이다.

void Union(int a, int b) {
	a = find(a);
	b = find(b);
	if(a != b)            //이미 친구 관계라면, 친구 수를 합하면 안됨
		cnt[a] += cnt[b]; //친구수의 합
	tree[b] = a;          //친구관계 형성
	cout << cnt[a] << "\n";
}

소스 코드


#include <iostream>
#include <map>
#include <string>
#include <cstring>
#include <stdio.h>

using namespace std;
int tree[100010];
int cnt[100010];

int find(int a) { 
	if (tree[a] == -1 || tree[a] == a)  
		return a;
	int root = find(tree[a]);
	return tree[a] = root;
}

void Union(int a, int b) { 
	a = find(a);
	b = find(b);
	if(a != b)              //친구 관계가 아닐 때
		cnt[a] += cnt[b];   //친구 수 합
	tree[b] = a;            //관계를 형성
	cout << cnt[a] << "\n";
}

int main() {

	ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    
	int T , F;
	cin >> T;

	for (int i = 0; i < T; i++) {
		cin >> F;
		map <string, int> name;
		memset(tree, -1, sizeof(tree));
		fill_n(cnt, 100010, 1);
		int idx = 1;
		for (int j = 0; j < F; j++) {
			string name1, name2;
			cin >> name1 >> name2;
			if (name.count(name1) == 0) 
				name[name1] = idx++;
			if (name.count(name2) == 0) 
				name[name2] = idx++;
			Union(name[name1], name[name2]);
		}
	}
}

개발 환경 : VS2019

질문, 지적 환영합니다!

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 1949 우수 마을  (0) 2020.06.12
[C++] 백준 2580 스도쿠  (0) 2020.06.10
[C++] 백준 14502 연구소  (0) 2020.06.06
[C++] 백준 17779 게리맨더링2  (0) 2020.05.29
[C++] 백준 17837 새로운 게임 2  (0) 2020.05.29

+ Recent posts