📝 문제
📌 풀이
- union-find를 이용하여 문제를 해결할 수 있다.
- 거짓말을 알고 있는 번호를 입력 받는다.
- 각 파티의 참여자들을 같은 union을 이용하여 같은 그룹으로 합친다.
- 각 파티를 다시 순회하면서 각 파티의 맨 앞 노드 번호의 부모 노드가 거짓말을 알고 있는 번호의 부모 노드와 같다면 거짓말을 할 수 없다는 것을 이용하여 거짓말을 할 수 있는 파티의 수를 센다.
거짓말을 알고 있는 번호들의 부모 노드와 비교해야 한다는 것이 중요하다. 그 이유는 union-find의 경우 노드 번호가 작은 것으로 부모 노드를 설정하여 합치게 되는데 만약 거짓말을 아는 사람의 노드 번호는 3번이고 파티의 참여자가 1, 2, 3번이라면 1번으로 합쳐질텐테 이 경우 거짓말을 할 수 없는 경우인데도 거짓말을 할 수 있다고 판단한다. 그 예제가 바로 7번 예제이다.
💻 코드
#include <iostream>
#include <vector>
using namespace std;
int parent[51];
int getParent(int n) {
if (parent[n] == n) {
return n;
}
else {
return parent[n] = getParent(parent[n]);
}
}
int checkParent(int a, int b) {
a = getParent(a);
b = getParent(b);
if (a == b) {
return 1;
}
else {
return 0;
}
}
void unionParent(int a, int b) {
a = getParent(a);
b = getParent(b);
if (!checkParent(a, b)) {
if (a < b) {
parent[b] = a;
}
else {
parent[a] = b;
}
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
vector<int> v[50];
int know[51] = { 0 };
int N, M;
cin >> N >> M;
int T;
cin >> T;
for (int i = 0; i < T; i++) {
cin >> know[i];
}
for (int i = 0; i < M; i++) {
int people_num;
cin >> people_num;
for (int j = 0; j < people_num; j++) {
int tmp;
cin >> tmp;
v[i].push_back(tmp);
}
}
for (int i = 1; i <= N; i++) {
parent[i] = i;
}
for (int i = 0; i < M; i++) {
if (v[i].size() == 1) {
continue;
}
else {
int num = v[i][0];
for (int j = 1; j < v[i].size(); j++) {
unionParent(num, v[i][j]);
}
}
}
int cnt = 0;
for (int i = 0; i < M; i++) {
bool fail = false;
int p = getParent(v[i][0]);
for (int j = 0; j < T; j++) {
if (getParent(know[j]) == p) {
fail = true;
break;
}
}
if (!fail) {
cnt++;
}
}
cout << cnt << '\n';
return 0;
}