[백준] 10775 공항

단계별로 풀어보기 문제를 보다가 내가 예전에 틀린 문제가 있어서 가져와보았다! 유니온 파인드 개념을 쓰는 문제였는데, 유니온 파인드 개념을 전혀 기억을 못해서 유니온 파인드 개념부터 공부해서 풀었다. 차근차근 한문제씩 풀어 나가는 게 나의 목표이다.

문제 설명

링크 공항에 비행기 p대가 있고, 게이트가 g개 있다. 문제만 읽으면 잘 이해가 가지 않는데, i번째 비행기를 1-g_i번째 게이트에는 무조건 도킹할 수 있어야 하고 도킹할 수 없으면 공항이 폐쇄된다고 한다. (g_i가 input으로 주어지게 된다) 이 말은 즉 i번째 비행기에 대해 input g_i가 들어오고, 이 i번째 비행기는 1-g_i번째 게이트에 세울 수 있다. 최대 몇 대의 비행기를 세울 수 있는지를(세울 수 없어서 공항 폐쇄 직전까지) 답으로 내면 된다.

문제 풀이

유니온 파인드 개념을 알았더라도 풀기 어려웠던 문제 같다.

먼저 유니온 파인드 개념이 무엇이냐면, 그래프 알고리즘에서 등장한다.

  • array[i] : i번째 노드의 부모 노드(루트 노드)

라고 했을 때, union이 두개의 disjoint set(루트 노드가 다른 두 그래프)를 합치는 것이고, find가 해당 그래프의 루트 노드를 return 하는 함수이다. 결국 부모 노드를 잘 다룰 수 있으면 된다. 따라서 union(x, y)는 x와 y의 루트 노드들을 각각 찾고(px, py라고 하겠다) px의 부모 노드를 py로 설정해주면 된다(array[px]=py). find함수는 array[i]=i인 경우 자신의 루트노드이므로 탐색을 멈추고 자신을 return하면 되고, 자신의 루트노드가 아닌 경우는 i의 부모 노드(array[i])에 대해 find함수를 재귀로 한번 더 돌려주면 된다. 여기서 중요한 점은 시간초과를 해결하는 것인데, 메모이제이션을 이용해 return 직전에 i의 부모 노드를 find(i)로 설정해 주면 된다. 즉 부모 노드가 현재 i의 부모 노드의 부모 노드일 수 있기 때문에, 계속 갱신해주면 된다.

아래 문제는 유니온 파인드 개념을 사용하면 풀 수 있는데, 나는 바로 떠올리지는 못했다. i번째 비행기는 1-g_i게이트들에 위치할 수 있는데, 이미 누군가 g_i게이트에 비행기를 세워 놓았으면 g_i-1에 비행기를 세워야 한다. 따라서 각 비행기를 세울 때마다 몇 번째 게이트에 세울 지 또 탐섹해야하기 때문에 O(p*g) 시간이 걸리게 된다. 따라서 각각의 비행기에 대해 몇 번째 게이트에 세우면 되는지 바로 알 수 있어야 한다. 이것은 어떻게 할 수 있냐면, 해당 비행기가 특정 게이트 x에 세워 졌다면, 그 다음 비행기는 무조건 x-1에 세워야 하므로 특정 게이트 x와 x전에 세워진 게이트들과, x-1번째 게이트를 union시켜주면 된다. 즉 내가 세운 게이트의 부모 노드를 내가 세운 게이트 바로 전 노드로 설정해주면 된다. 이렇게 하면 이후에 내가 x번째 게이트에 다시 세우려고 시도할 때, x-1번째 게이트의 부모부터 고려할 수 있게 된다.

코드는 아래와 같다.

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
#include <cstdio>

int a[100001];

int find_(int x){
    if(a[x]==x) return x;
    return a[x] = find_(a[x]);
}

void union_(int x, int y){
    a[find_(x)]=find_(y);
}

int main(){
    int g, p;
    scanf("%d%d", &g, &p);

    for(int i=1;i<=g;i++) a[i]=i;

    int ans=0;
    while(p--){
        int tmp;
        scanf("%d", &tmp);

        if(find_(tmp)==0) break;

        ans++;
        union_(find_(tmp), find_(tmp)-1);
    }

    printf("%d", ans);
}