Post

baekjoon 10775:공항

baekjoon 10775 공항

10775번 공항

접근

문제 크기게 굉장히 크기 때문에 일단 dp는 될 수 없다고 생각했다.
처음엔 1번째 gate부터 넣고 더 여유있는 비행기들이 뒤로 옮기는 방식으로 했다.
왜 처음부터 가장 자기가 갈 수 있는 최대로 안갔냐고 묻는다면 어짜피 거기서도 많이 차면 아래로 한칸씩 내려와야한다고 생각했다.
하지만 이렇게는 결국 O(n^2)밖에 되지 않았다. 1초안에 풀려면 최소 O(nlogn)은 되야 했다.
그래서 찾아보니 2가지 방법이 있었다.
각 칸에 몇칸 뛰어 넘어야하는지 적으면서 하는 방법이 있었고
또하나는 union-find로 같이 있는 비행기들을 한 묶음으로 만들고 그들이 모두 그다음 빈칸을 가리키도록 하는 방법이다.
사실 그냥 union-find를 쓰면 이것도 다 돌기 때문에 O(n^2)이지만 union-find에 path compression을 적용하여 모두 다 root node로 직접연결 시켜버렸다.
이렇게 되면 빈칸 찾는데 걸리는 시간이 가장 빠르면 O(1), 평균 O(logn)으로 충분히 빠르게 찾을 수 있게 된다.

시간 초과 된 코드

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
#include <iostream>
#include <vector>
using namespace std;
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0); cout.tie(0);
  int g, p; cin >> g >> p;
  vector<pair<int, int>> gates(g);
  for(auto& i: gates){
    i.first = i.second = -1;
  }
  int ans = 0;
  for(int i = 0; i < p; i++){
    int a; cin >> a;
    pair<int, int> curr = {i, a-1};
    bool inserted = false;
    for(int j = 0; j < g; j++){
      if(gates[j].first == -1 && curr.second >= j){
        gates[j] = curr;
        inserted = true;
        ans++;
        break;
      }
      else{
        if(gates[j].second > curr.second && curr.second >= j){
          pair<int, int> temp = gates[j];
          gates[j] = curr;
          curr = temp;
          continue;
        }
      }
    }
    if(!inserted){
      cout << ans;
      return 0;
    }
  }
  cout << ans;
  return 0;
}

성공 코드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
33
#include <iostream>
#include <vector>
using namespace std;
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0); cout.tie(0);
  int g, p; cin >> g >> p;
  vector<pair<int, int>> gates(g);
  vector<int> gatesClever(g);
  for(auto& i: gates){
    i.first = i.second = -1;
  }
  int ans = 0;
  for(int i = 0; i < p; i++){
    int a; cin >> a;
    pair<int, int> curr = {i, a-1};
    int j = a-1;  
    while(gatesClever[j] > 0 && j>=0){
      int temp = gatesClever[j];
      gatesClever[j]++;
      j -= temp;
    }
    if(j < 0){
      cout << ans;
      return 0;
    }
    gates[j] = curr;
    gatesClever[j]++;
    ans++;
  }
  cout << ans;
  return 0;
}

성공 코드2

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
#include <iostream>
#include <vector>
using namespace std;
int find(int x, vector<int>& gatesUf){
  if(gatesUf[x] == x) return x;
  return gatesUf[x] = find(gatesUf[x], gatesUf);
}
bool merge(int a, int b, vector<int>& gatesUf){
  a = find(a, gatesUf);
  b = find(b, gatesUf);
  if(a == b) return false;
  gatesUf[b] = a;
  return true;
}
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0); cout.tie(0);
  int g, p; cin >> g >> p;
  vector<bool> gates(g+1);
  vector<int> gatesUf(g+1);
  for(int i = 0; i < g+1; i++) gatesUf[i] = i;
  int ans = 0;
  for(int i = 0; i < p; i++){
    int a; cin >> a; 
    int t = find(a, gatesUf);
    if(t == 0){
      cout << ans;
      return 0;
    }
    gates[t] = true;
    ans++;
    merge(t-1, t, gatesUf);
  }
  cout << ans;
  return 0;
}

배운 점

union-find에 대해 응용도 해보았고, union-find가 분리 집합을 만드는 알고리즘 중 하나라는 것을 알게 되었다.
union-find의 구현도 여러가지 방법이 있다는 것을 알게 되었다.

This post is licensed under CC BY 4.0 by the author.