알고리즘

백준 1043: 거짓말 [C++]

avocado8 2024. 7. 24. 14:21

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

 

거짓말쟁이가 되기시러

각 파티의 참여자를 서로 연결하고, 타고타고 올라갔을때 진실을 아는 사람을 만나는지 안 만나는지를 확인해야 한다. 연결하고 찾는데는 유니온파인드를 사용한다.

보자마자 이거다 하고 생각이 안 났다.. 사실 지금도 잘은 모르겠다 첨엔 걍 단순히 진실을 아는 사람이 파티에 섞여있는가를 따지는 문제인가 싶었는데, 같은 사람이 다른 파티에 참여해 다른 사실을 듣는 것 역시 안 된다는 조건 때문에 유니온파인드를 사용해줘야 했다.

 

우선 유니온파인드 구현은 언제나와 같다.

vector<int>parent;

int findParent(int node) {
    if (parent[node] < 0) {
        return node;
    }
    return parent[node] = findParent(parent[node]);
}

// Union two sets
void unionInput(int x, int y) {
    int xp = findParent(x);
    int yp = findParent(y);
    if (xp == yp) return; // 이미 같은 집합에 속해 있다면 return
    if (parent[xp] < parent[yp]) {
        swap(xp, yp);
    }
    parent[yp] += parent[xp];
    parent[xp] = yp;
}

 

입력으로 진실을 아는 사람들이 주어진다. 이 사람들을 know_truth라는 배열에 따로 저장해준다.

for(int i=0;i<truth_cnt;i++){
        cin >> know_truth[i];
    }

 

이제 각 파티의 참여자 정보가 주어진다. 벡터형태로 여러 개 주어질 것이므로 이차원벡터로 저장해준다.

vector<vector<int>> parties(m, vector<int>(n+1, 0));
    for(int i=0;i<m;i++){
        cin >> party_size;
        parties[i].resize(party_size);
        for(int j=0;j<party_size;j++){
            cin >> parties[i][j];
        }
    }

 

각 파티의 사람들은 하나의 집합으로 연결되어야 한다. union을 수행해주자.

for(auto party: parties){
        for(int j=1;j<party.size();j++){
            unionInput(party[0], party[j]);
        }
    }

 

연결을 다 했으면 이제 진실을 아는 사람들과 연결점이 있는지 찾아줘야 한다.

그러기 위해서는 먼저 진실을 아는 사람들도 하나의 집합으로 연결되어야 할 필요가 있다.

for(int i=1;i<truth_cnt;i++){
        unionInput(know_truth[0], know_truth[i]);
    }

이렇게 연결된 후, 진실을 아는 사람들이 연결된 집합의 루트를 찾아준다. findParent를 쓰면 되지만 진실을 아는 사람이 0명 주어질 수도 있으므로, 그럴 경우에는 초기값인 -1을 할당해준다

int truth_root = know_truth.empty() ? -1 : findParent(know_truth[0]);

 

이제 주어진 파티에서 과장해 말할 수 있는지 체크해준다.

먼저 true로 설정한 뒤, 각 파티 내에서 반복문을 돌며 노드의 부모를 찾아주고, 해당 부모가 진실을 아는 사람 집단의 루트(truth_root)와 같은지 확인해준다. 같다면 언젠가는 이 사람은 진실을 알게 될 것이므로 false로 바꿔준다. 한 명만 false로 판명나도 그 파티에서는 과장해 말할 수 없으므로 반복을 탈출해준다.

한 파티에서 검사가 끝난 뒤에도 flag가 여전히 true라면 그 파티에서는 과장해도 되므로 answer 값을 증가시킨다.

이 과정을 모든 파티에 대해 반복한다.

int answer = 0;
    for(auto party:parties){
        bool flag = true; //해당 파티에서 과장할 수 있는지
        for(auto p : party){
            if(findParent(p) == truth_root){
                flag = false; //해당 노드의 부모가 진실을 아는 집단의 부모와 동일 => 과장할 수 없음
                break;
            }
        }
        if(flag) answer++;
    }

 

전체 코드

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

vector<int>parent;

int findParent(int node) {
    if (parent[node] < 0) {
        return node;
    }
    return parent[node] = findParent(parent[node]);
}

// Union two sets
void unionInput(int x, int y) {
    int xp = findParent(x);
    int yp = findParent(y);
    if (xp == yp) return; // 이미 같은 집합에 속해 있다면 return
    if (parent[xp] < parent[yp]) {
        swap(xp, yp);
    }
    parent[yp] += parent[xp];
    parent[xp] = yp;
}

int main(){
    int n, m, truth_cnt, party_size;
    cin >> n>> m >> truth_cnt;
    parent.assign(n+1, -1);
    vector<int> know_truth(truth_cnt); //진실을 아는 사람들
    
    for(int i=0;i<truth_cnt;i++){
        cin >> know_truth[i];
    }
    
    //파티 참여자 정보 입력
    vector<vector<int>> parties(m, vector<int>(n+1, 0));
    for(int i=0;i<m;i++){
        cin >> party_size;
        parties[i].resize(party_size);
        for(int j=0;j<party_size;j++){
            cin >> parties[i][j];
        }
    }
    
    //각 파티의 사람들을 한 집합으로 union
    for(auto party: parties){
        for(int j=1;j<party.size();j++){
            unionInput(party[0], party[j]);
        }
    }
    
    for(int i=1;i<truth_cnt;i++){
        unionInput(know_truth[0], know_truth[i]);
    }
    int truth_root = know_truth.empty() ? -1 : findParent(know_truth[0]);
    
    int answer = 0;
    for(auto party:parties){
        bool flag = true; //해당 파티에서 과장할 수 있는지
        for(auto p : party){
            if(findParent(p) == truth_root){
                flag = false; //해당 노드의 부모가 진실을 아는 집단의 부모와 동일 => 과장할 수 없음
                break;
            }
        }
        if(flag) answer++;
    }
    cout << answer;
    return 0;
    
    
}

 

 

'알고리즘' 카테고리의 다른 글

백준 1655: 가운데를 말해요 [C++]  (1) 2024.07.26
백준 1052: 물병 [C++]  (0) 2024.07.25
백준 1260: DFS와 BFS [C++]  (0) 2024.07.19
백준 10026: 적록색약[C++]  (0) 2024.07.18
백준 7662: 이중 우선순위 큐 [C++]  (0) 2024.07.17