알고리즘 문제/[백준]

[백준] 20119번: 클레어와 물약

latter2005 2020. 12. 3. 19:35

경북대학교 고리콘 대회 문제 : www.acmicpc.net/contest/view/545

 

2020 경북대학교 Goricon

사용 가능한 언어 C++17 Java 8 Python 3 C11 PyPy3 C99 C++98 C++11 C++14 Java 8 (OpenJDK) Java 11 C++20 Python 2 PyPy2 C99 (Clang) C++98 (Clang) C++11 (Clang) C++14 (Clang) C11 (Clang) C++17 (Clang) C++20 (Clang) C90 C2x C90 (Clang) C2x (Clang)

www.acmicpc.net

문제 주소 : www.acmicpc.net/problem/20119

 

20119번: 클레어와 물약

첫 번째 줄에는 세상에 존재하는 물약의 종류의 수 N (3 ≤ N ≤ 200,000) 과 클레어가 알고 있는 레시피의 개수 M (1 ≤ M ≤ 200,000) 이 주어진다. 다음 M개의 줄에는 각각의 줄마다 레시피의 정보 ki,

www.acmicpc.net

문제 확인

 

그래프 문제

시뮬레이션 문제처럼 주어진 대로 풀면 쉽게 풀리는 문제입니다.

레시피에서 필요한 물약들을 간선으로 만들고 가지고 있는 물약마다 간선을 제거하여 간선 수가 0이 될 시 가지고 있는 물약에 추가합니다.

문제 예시 중 이미 가지고 있는 물약을 레시피로 들고 있는 경우도 있는데 이를 위하여 중복체크를 해 주어야 합니다.

 

풀이

 

문제를 2개의 스텝으로 분리합니다.

  1. 필요한 물약을 간선으로 두는 그래프 만들기
  2. 가지고 있는 물약으로 간선을 제거하며 새로운 물약이 만들어지면 다음 루프로 진입, 만들어진 물약이 없다면 루프 종료

2번 스텝에서 간선을 제거하기 때문에 각 루프마다 가지고 있는 모든 물약을 탐색할 필요가 없습니다. 새로 생성된 물약만 체크하고 생성된 물약이 없다면 종료합니다. 모든 물약을 검사하게 되면 시간 초과가 발생합니다.

 

문제에서 오름차순으로 출력하라고 하여 set 혹은 map을 사용하게 되면 물약을 추가할 때마다 탐색을 하기 때문에 시간, 메모리 모두 비효율적입니다. 따라서 vector을 사용 후 마지막에 정렬합니다.

 

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
 
#include <unistd.h>
namespace io {
    const signed IS = 1 << 22, OS = 1 << 20;
    char I[IS + 1], *= I, O[OS], *= O;
 
    inline void daer() { if (J >= I + IS - 64) { char*= I; do*p++ = *J++while (J != I + IS); p[read(0, p, I + IS - p)] = 0; J = I; } }
    template<int N = 10typename T = int> inline T getu() { daer(); T x = 0int k = 0do x = x * 10 + *- '0'while (*++>= '0'&&++< N); ++J; return x; }
    template<int N = 10typename T = int>inline  T geti() { daer(); bool e = *== '-'; J += e; return(e ? -1 : 1)*getu<N, T>(); }
    inline void flush() { write(1, O, K - O); K = O; }
 
    template<int N = 10typename T = int> inline void putu(T n) { char s[(N + 7/ 8 * 8], *= s; int k = 0do*p++ = n % 10 + 48while ((n /= 10&& ++< N); do*K++ = *--p; while (p != s); *K++ = 10if (K >= O + OS - 64)flush(); }
    template<int N = 10typename T = int> inline void puti(T n) { if (n < 0)*K++ = '-', n = -n; putu<N, T>(n); }
    struct f { f() { I[read(0, I, IS)] = 0; }~f() { flush(); } }flu;
};//출처 : https://cgiosy.github.io/posts/fast-io
using namespace io;
typedef struct recp {
    int size, id;
}recp;
int main() {
    int n = getu(), m = getu(), t;
    bool dup_check[200001= { 0 }; //물약 중복 체크
    vector<int> res;
    vector<vector<int>>graph(n + 1);
    vector<recp> recp_list(m);
    for (int i = 0; i < m; i++) {
        recp_list[i].size = getu();//0이 될 시 완성
        for (int j = 0; j < recp_list[i].size; j++) {
            graph[getu()].push_back(i);//간선 생성
        }
        recp_list[i].id = getu();//물약 종류
    }
    t = getu();
    for (int i = 0; i < t; i++) {
        int tmp = getu();
        res.push_back(tmp);
        dup_check[tmp] = true;
    }
    int start = 0end = res.size();
    do {
        for (int i = start; i < end; i++) {
            for (auto target : graph[res[i]]) {
                recp_list[target].size--//중복체크를 하기 때문에 간선 개수로만 검사할 수 있음
                if (recp_list[target].size == 0 && !dup_check[recp_list[target].id]) {//물약 생성, 중복체크
                    res.push_back(recp_list[target].id);
                    dup_check[recp_list[target].id] = true;
                }
            }
        }
        start = end//전에 검사한 물약은 다시 검사하지 않음
        end = res.size();
    } while (start != end);//생성된 물약이 없다면 종료
    sort(res.begin(), res.end());
 
    printf("%d\n", res.size());
    for (auto t : res)
        printf("%d ", t);
}
cs
반응형