1. Approach
(1) 재귀 + Union Find 알고리즘 으로 접근한다.
2. DP[N][M] : n 명의 사람이 m개의 파티를 참가할 때 지민이가 최대한 과장되게 이야기를 할 수 있는 파티 개수의 최댓값. ( 이 문제에서 시간이 충분해서 DP는 큰 의미가 없다 )
-> DP[N][4] : N행, 1 - 3 열까지의 정점까지는 오는데에 필요한 최소 비용
(3) 풀이 방법
해결 방법 : #UnionFind 알고리즘을 사용한다. ( #서로소집합 )
(1) 진실을 알고있는 사람들과 같은 파티에 참가한 사람들을 모두 그래프로 잇는다.
(2) 그래프가 이어진 사람들에게는 지민이가 과장된 이야기 ( 거짓말 )를 할 수가 없다.
(3) 그래프가 이어진 사람들을 알고있는 사람들이라고 전부 생각하고 조건을 두면 해결할 수 있다.
※ Union Find 알고리즘은 그래프 알고리즘에서 매우 많이 쓰이고 여러 알고리즘의 기반이 되므로 공부하시길 권장합니다.
만약 위 알고리즘을 모른 상태로 아래 코드를 본다면 지민이가 과장된 이야기를 할 수 없는 사람들을 전부 연결해놨다고 가정하면 될것입니다.
2. Source Code
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
#define MAX 51
using namespace std;
// https://www.acmicpc.net/problem/1043
int n, m;
int nknown;
bool known[MAX];
vector<int> partyinfo[MAX];
int dp[MAX][MAX];
//////////////////////////////////// 아래 함수 3개는 Union Find 알고리즘을 위한 함수
// x의 부모 노드를 찾는 함수 << 부모 노드를 갱신해주는 함수
// Union Find 알고리즘으로 서로 연결되어있나 확인해본다.
int parent[MAX];
int getParent(int parent[], int x)
{
// 해당 부모 값과 x값이 같은 경우
if (parent[x] == x) return x; // 1로 모두 연결하면 이 부분을 통과하는 것은 1이될 것이다.
// 최종 부모를 찾는 과정
return parent[x] = getParent(parent, parent[x]);
}
// 두 부모 노드를 합치는 함수 << 노드간에 연결해주는 함수
void unionParent(int parent[], int a, int b)
{
a = getParent(parent, a);
b = getParent(parent, b);
if (a < b) parent[b] = a;
else parent[a] = b;
}
// 같은 부모를 가지는지 확인 << 노드간에 간선으로 이뤄져있는지 확인하는 함수
int findParent(int parent[], int a, int b)
{
a = getParent(parent, a);
b = getParent(parent, b);
if (a == b) return 1;
return 0;
}
//////////////////////////////////// 위 3개 함수는 Union Find 알고리즘을 위한 함수
int solution(int peopleidx, int partyidx)
{
// 파티에서 거짓말할 수 있는 경우
if (peopleidx == 0) return 1;
int& result = dp[peopleidx][partyidx];
if (result != -1)
return result;
result = 0;
// 현재 peopleidx인 사람이 거짓말을 알고 있고
if (known[peopleidx])
{
// 이 파티에 참가해있는 인원들을 확인해본다
for (int i = 0; i < partyinfo[partyidx].size(); i++)
{
if (peopleidx == partyinfo[partyidx][i])
{
// 이 경우 거짓말을 할 수 없는 파티가 된다.
return 0;
}
}
// 파티에 참가해있는 인원중 없는 인원이라면 가능
result = solution(peopleidx - 1, partyidx);
}
else {
// 모르는 사람이라면 가능
result = solution(peopleidx - 1, partyidx);
}
return result;
}
int main()
{
cin >> n >> m;
cin >> nknown;
vector<int> knownclub;
for (int i = 1; i <= n; i++)
{
parent[i] = i;
}
for (int i = 1; i <= nknown; i++)
{
int people; cin >> people;
knownclub.push_back(people);
known[people] = true;
}
for (int i = 1; i <= m; i++)
{
int many, who; cin >> many;
for (int j = 0; j < many; j++)
{
cin >> who;
partyinfo[i].push_back(who);
// 같은 파티에 참가하는 인원끼리 전부 그래프로 엮어준다.
unionParent(parent, partyinfo[i][0], partyinfo[i][j]);
}
}
// 사실을 알고있는 사람과 엮인 사람들의 known 배열을 전부 true로 바꿔준다.
for (int j = 0; j < knownclub.size(); j++)
{
for (int i = 1; i <= n; i++)
{
// 엮인 경우
if (findParent(parent, knownclub[j], i) == 1)
{
known[i] = true;
}
}
}
int ans = 0;
for (int i = 1; i <= m; i++)
{
memset(dp, -1, sizeof(dp));
ans += solution(n, i);
}
cout << ans << endl;
return 0;
}
3. 비슷한 문제
- DFS 관련 문제
4. 참조
'Algorithm > Search Algorithm' 카테고리의 다른 글
[백준] 4963번 섬의 개수 (0) | 2020.08.15 |
---|