PS/백준

[BOJ] 2533 사회망 서비스(SNS)

uyt8989 2022. 1. 9. 13:16

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

 

2533번: 사회망 서비스(SNS)

첫 번째 줄에는 친구 관계 트리의 정점 개수 N이 주어진다. 단, 2 ≤ N ≤ 1,000,000이며, 각 정점은 1부터 N까지 일련번호로 표현된다. 두 번째 줄부터 N-1개의 줄에는 각 줄마다 친구 관계 트리의 에

www.acmicpc.net

문제

페이스북, 트위터, 카카오톡과 같은 사회망 서비스(SNS)가 널리 사용됨에 따라, 사회망을 통하여 사람들이 어떻게 새로운 아이디어를 받아들이게 되는가를 이해하는 문제가 중요해졌다. 사회망에서 사람들의 친구 관계는 그래프로 표현할 수 있는데,  이 그래프에서 사람은 정점으로 표현되고, 두 정점을 잇는 에지는 두 정점으로 표현되는 두 사람이 서로 친구 관계임을 표현한다. 

예를 들어, 철수와 영희, 철수와 만수, 영희와 순희가 서로 친구 관계라면 이를 표현하는 친구 관계 그래프는 다음과 같다. 

친구 관계 그래프를 이용하면 사회망 서비스에서 어떤 새로운 아이디어가 전파되는 과정을 이해하는데 도움을 줄 수 있다. 어떤 새로운 아이디어를 먼저 받아들인 사람을 얼리 아답터(early adaptor)라고 하는데, 사회망 서비스에 속한 사람들은 얼리 아답터이거나 얼리 아답터가 아니다. 얼리 아답터가 아닌 사람들은 자신의 모든 친구들이 얼리 아답터일 때만 이 아이디어를 받아들인다. 

어떤 아이디어를 사회망 서비스에서 퍼뜨리고자 할 때, 가능한 한 최소의 수의 얼리 아답터를 확보하여 모든 사람이 이 아이디어를 받아들이게 하는  문제는 매우 중요하다. 

일반적인 그래프에서 이 문제를 푸는 것이 매우 어렵다는 것이 알려져 있기 때문에, 친구 관계 그래프가 트리인 경우, 즉 모든 두 정점 사이에 이들을 잇는 경로가 존재하면서 사이클이 존재하지 않는 경우만 고려한다. 

예를 들어, 8명의 사람으로 이루어진 다음 친구 관계 트리를 생각해보자. 2, 3, 4번 노드가 표현하는 사람들이 얼리 아답터라면, 얼리 아답터가 아닌 사람들은 자신의 모든 친구가 얼리 아답터이기 때문에 새로운 아이디어를 받아들인다.

친구 관계 트리가 주어졌을 때, 모든 개인이 새로운 아이디어를 수용하기 위하여 필요한 최소 얼리 어답터의 수를 구하는 프로그램을 작성하시오.

입력

첫 번째 줄에는 친구 관계 트리의 정점 개수 N이 주어진다. 단, 2 ≤ N ≤ 1,000,000이며, 각 정점은 1부터 N까지 일련번호로 표현된다. 두 번째 줄부터 N-1개의 줄에는 각 줄마다 친구 관계 트리의 에지 (u, v)를 나타내는 두 정수 u와 v가 하나의 빈칸을 사이에 두고 주어진다. 

8
1 2
1 3
1 4
2 5
2 6
4 7
4 8

출력

주어진 친구 관계 그래프에서 아이디어를 전파하는데 필요한 얼리 아답터의 최소 수를 하나의 정수로 출력한다.

3

DP를 활용하는 문제였다.

문제를 보자마자 이 문제는 결국 나 혼자 못 풀고 블로그를 참고해야 할 것 같은 느낌이 팍 들었다.

 

1. 

조건에 N이 꽤 큰 숫자로 주어져서 자료구조를 인접 행렬로는 쓸 수 없겠다는 생각이 들었다.

그래서 떠올린 것이 인접 리스트인데 구현하기가 너무 귀찮았다.

그런데 C++에는 벡터라는 진짜 야무진 친구가 있어서 냉큼 사용했다.

 

2.

어떤 방식으로 풀어야할지 곰곰이 생각해보다 이 문제를 어디선가 본 것 같은 느낌이 들었다.

학기 중에 배웠던 Maximal Indepent Set을 구하는 알고리즘과 느낌이 비슷했다.

근데 그 알고리즘을 직접 작성해보지는 않았기 때문에 제대로 기억이 나지 않았다.

그런데 실제로 정답 코드를 살펴보니 비슷하긴 하더라.

조금 더 MIND 코드를 제대로 이해했었다면 풀 수 있을 법도 했다.

 

0은 얼리어답터, 1은 일반인으로 나눠서 풀 수 있었다.

재귀 함수를 호출해서 leaf에서부터 root로 올라온다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <vector>
#include <algorithm>

using namespace std;

#define MAX 1000001

int N;
int u, v;
vector<int> edge[MAX];
int dp[MAX][2];
bool visited[MAX];

void find(int x) {
	visited[x] = true;
	dp[x][0] = 1;

	for (int i = 0; i < edge[x].size(); i++) {
		int child = edge[x][i];
		if (visited[child]) continue;
		find(child);

		dp[x][1] += dp[child][0];
		dp[x][0] += min(dp[child][0], dp[child][1]);
	}
}

int main() {
	scanf("%d", &N);

	for (int i = 0; i < N - 1; i++) {
		scanf("%d %d", &u, &v);
		edge[u].push_back(v);
		edge[v].push_back(u);
	}

	find(1);

	printf("%d\n", min(dp[1][0], dp[1][1]));

	return 0;
}

 

후기)

벡터라는 자료구조를 떠올리는데에 드는 시간이 너무 오래 걸렸다.

좀 더 익숙했다면 인접 리스트 대신에 바로 떠올렸을 텐데.

 

아마 트리 DP는 다 이런 식이지 않을까 싶은데 그건 다른 문제도 풀어봐야 알 수 있을 것 같다.

 

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

[BOJ] 2252 줄 세우기  (2) 2022.01.10
[BOJ] 1949 우수 마을  (2) 2022.01.09
[BOJ] 1562 계단 수  (1) 2022.01.08
[BOJ] 1043 거짓말  (4) 2022.01.07
[BOJ] 2357 최솟값과 최댓값  (0) 2022.01.06