본문 바로가기
알고리즘

백준 g4 1043 거짓말 c++

by kyj0032 2024. 3. 19.

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

 

1043번: 거짓말

지민이는 파티에 가서 이야기 하는 것을 좋아한다. 파티에 갈 때마다, 지민이는 지민이가 가장 좋아하는 이야기를 한다. 지민이는 그 이야기를 말할 때, 있는 그대로 진실로 말하거나 엄청나게

www.acmicpc.net

 

문제

사람 수 N과 진실을 아는 사람들 번호가 주어진다.

M개의 파티가 주어진다.

 

각각의 파티에서 지민이는 1. 진실을 아는 사람들이 왔을 때 2. 다른 곳에서 진실을 얘기했는데 똑같은 사람이 파티에 왔을 때, 그 파티에서 거짓말이 아니라 진실을 말해야 한다.

지민이가 거짓말을 할 수 있는 파티의 개수는?

 

풀이

문제를 정리하면, 진실을 아는 사람과 한 번이라도 같은 파티에 참여하는 사람들, 그런 사람들과 같이 파티에 참여하는 사람들(진실을 듣게 되므로) .... 연쇄적으로 같은 파티에 참여하는 사람들은 모두 진실을 들을 수 밖에 없다. 

이런 연쇄적인 경우를 셀 때 그래프가 유용한가보다

 

진실을 아는 사람들과 연결된 사람들의 관계를 그래프로 나타낸 뒤에, 그 사람들이 포함된 파티에선 거짓말을 하지 못한다고 보면 된다.

 

구현

1. adj 파티에서 참가한 대로 기록

- 이때 1, 2, 3 이렇게 참가했다면 인접리스트로 어떻게 기록할 지 고민이 되는데, 누가 누구랑 연결되어 있느냐인 간선이 중요한 게 아니라 연결되어있느냐 아니냐 자체가 중요하기 때문에 그냥 1 -> 2, 1 -> 3 으로 연결해도 상관 없다.

2. 아는 사람 list에서 BFS로 추가로 알게 되는 사람 기록

- 이때 추가로 알게 되는 사람도 BFS를 돌려야 하는가? 돌려야 한다. 왜냐하면 

ex. 1 -> 2 -> 5 / 5 -> 7의 경우 7도 진실을 아는 사람에 포함되어야 하기 때문이다.

3. 아는 사람이 없는 파티 개수 cnt

 

 

+ 주의할 점

for (auto e : a)
{
    a.push_back(...);
}

이런 형식의 range based for loops는 쓸 수 없다.

단순히 무한루프를 떠나서, 

for문은 컨테이너의 시작 ~ 끝으로 도는데 컨테이너의 크기가 변경되면 예상치 못한 원소에 접근할 수 있기 때문에 이상한 값이 나온다.

 

대신 index based for loops를 쓰도록 하자

 

전체 코드

/*boj g4 1043 거짓말*/
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
#define MAXN 52
using namespace std;

int N, M, K;
vector<int> know;
vector<int> adj[MAXN];
vector<int> party[MAXN];
int vis[MAXN];
int knowCheck[MAXN];

int main(void) {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> N >> M;
    cin >> K;
    for (int i = 0; i < K; i++) {
        int x;
        cin >> x;
        know.push_back(x);
    }
    // 1. adj에 파티 참가한 사람들끼리 연결
    for (int i = 0; i < M; i++) {
        int n;
        cin >> n;
        int x, y;
        cin >> x;
        party[i].push_back(x);
        while (--n) {
            cin >> y;
            adj[x].push_back(y);
            adj[y].push_back(x);

            party[i].push_back(y);
        }
    }

    // 2. 아는 사람 list에서 BFS로 연결되어 있는 사람들 기록
    for (int j = 0; j < know.size(); j++) {
        int k = know[j];
        queue<int> Q;

        Q.push(k);
        vis[k] = 1;

        while (!Q.empty()) {
            int cur = Q.front();
            Q.pop();

            for (int a : adj[cur]) {
                if (vis[a]) continue;

                Q.push(a);
                vis[a] = 1;
                know.push_back(a); // -> 이미 아는 사람 list인데 또 추가될 수 있음
            }
        }
    }

    for (int a : know) {
        knowCheck[a] = 1;
    }

    // 3. 아는 사람이 없는 파티 개수 cnt
    int answer = 0;
    for (int i = 0; i < M; i++) {
        bool flag = false;
        for (int p : party[i]) {
            if (knowCheck[p] == 1) {
                flag = true;
                break;
            }
        }
        if (flag == false)
            answer++;
    }

    cout << answer << "\n";
}

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

백준 g4 4803 트리 c++  (0) 2024.03.21
백준 g5 2166 다각형의 면적 c++  (0) 2024.03.20
백준 g4 2617 구슬 찾기 c++  (0) 2024.03.19
백준 g4 1707 이분 그래프 c++  (0) 2024.03.18
백준 s1 11403 경로 찾기 c++  (0) 2024.03.17