이 포스팅은 Algorithm Problem Solving 시리즈 113 편 중 9 번째 글 입니다.

  • Part 1 - 백준(1003번): 피보나치 함수
  • Part 2 - 백준(1010번): 다리놓기
  • Part 3 - 백준(1012번): 유기농 배추
  • Part 4 - 백준(10159번): 저울
  • Part 5 - 백준(10164번): 격자상의 경로
  • Part 6 - 백준(1018번): 체스판 다시 칠하기
  • Part 7 - 백준(1018번): 체스판 다시 칠하기
  • Part 8 - 백준(1022번): 소용돌이 예쁘게 출력하기
  • Part 9 - This Post
  • Part 10 - 백준(10816번): 숫자 카드2
  • Part 11 - 백준(10816번): 숫자카드 2
  • Part 12 - 백준(10819번): 차이를 최대로
  • Part 13 - 백준(10830번): 행렬 곱셈
  • Part 14 - 백준(10844번): 쉬운 계단수
  • Part 15 - 백준(10868번): 최소값
  • Part 16 - 백준(1092번): 배
  • Part 17 - 백준(11003번): 최솟값 찾기
  • Part 18 - 백준(1102번): 발전소
  • Part 19 - 백준(11048번): 이동하기
  • Part 20 - 백준(11053번): 가장 긴 증가하는 부분 수열
  • Part 21 - 백준(11054번): 가장 긴 바이토닉 부분 수열
  • Part 22 - 백준(11055번): 가장 긴 증가하는 부분 수열2
  • Part 23 - 백준(11057번): 오르막 수
  • Part 24 - 백준(1120번): 문자열
  • Part 25 - 백준(11403번): 경로 찾기
  • Part 26 - 백준(11404번): 플루이드
  • Part 27 - 백준(1149번): RGB 거리
  • Part 28 - 백준(11559번): puyo puyo
  • Part 29 - 백준(11655번): ROT13
  • Part 30 - 백준(1167번): 트리의 지름
  • Part 31 - 백준(11722번): 가장 감소하는 부분 수열
  • Part 32 - 백준(12015번): 가장 긴 증가하는 부분 수열(LIS) 2
  • Part 33 - 백준(12851번): 숨바꼭질2
  • Part 34 - 백준(12852번): 1로 만들기 2
  • Part 35 - 백준(12865번): 평범한 배낭
  • Part 36 - 백준(1300번): K번째 수
  • Part 37 - 백준(1325번): 효율적인 해킹
  • Part 38 - 백준(13549번): 숨바꼭질3
  • Part 39 - 백준(13913번): 숨바꼭질4
  • Part 40 - 백준(14002번): 가장 긴 증가하는 부분 수열 4
  • Part 41 - 백준(1431번): 시리얼 넘버
  • Part 42 - 백준(1436번): 영화감독 숌
  • Part 43 - 백준(14499번): 주사위 굴리기
  • Part 44 - 백준(14888번): 연산자 끼워넣기
  • Part 45 - 백준(14889번): 스타트와 링크
  • Part 46 - 백준(14891번): 톱니바퀴
  • Part 47 - 백준(15658번): 연산자 끼워넣기 2
  • Part 48 - 백준(15686번): 치킨 배달
  • Part 49 - 백준(1697번): 숨바꼭질
  • Part 50 - 백준(1697번): 숨바꼭질
  • Part 51 - 백준(1707번): 이분 그래프(Bipartite Graph)
  • Part 52 - 백준(1708번): 볼록 껍질
  • Part 53 - 백준(17136번): 색종이 붙이기
  • Part 54 - 백준(1717번): 집합의 표현
  • Part 55 - 백준(17298번): 오큰수
  • Part 56 - 백준(17626번): Four Squares
  • Part 57 - 백준(18870번): 좌표 압축
  • Part 58 - 백준(1890번): 점프
  • Part 59 - 백준(1918번): 후위 표기식
  • Part 60 - 백준(1929번): 소수 구하기
  • Part 61 - 백준(1963번): 소수 경로
  • Part 62 - 백준(1965번): 상자넣기
  • Part 63 - 백준(1976번): 여행가자
  • Part 64 - 백준(1987번): 알파벳
  • Part 65 - 백준(1992번): 쿼드트리
  • Part 66 - 백준(2003번): 수들의 합2
  • Part 67 - 백준(20040번): 사이클 게임
  • Part 68 - 백준(2042번): 구간 합 구하기
  • Part 69 - 백준(2108번): 통계학
  • Part 70 - 백준(2110번): 공유기 설치
  • Part 71 - 백준(2156번): 포도주 시식
  • Part 72 - 백준(2193번): 이친수
  • Part 73 - 백준(2231번): 분해합
  • Part 74 - 백준(2250번): 트리의 높이와 너비
  • Part 75 - 백준(2293번): 동전 1
  • Part 76 - 백준(2294번): 동전 2
  • Part 77 - 백준(2343번): 기타 레슨
  • Part 78 - 백준(2468번): 안전 영역
  • Part 79 - 백준(2512번): 예산
  • Part 80 - 백준(2529번): 부등호
  • Part 81 - 백준(2565번): 전깃줄
  • Part 82 - 백준(2581번): 소수
  • Part 83 - 백준(2583번): 영역구하기
  • Part 84 - 백준(2630번): 색종이 만들기
  • Part 85 - 백준(2644번): 촌수계산
  • Part 86 - 백준(2667번): 단지번호붙이기
  • Part 87 - 백준(2751번): 수 정렬하기 2
  • Part 88 - 백준(2798번): 블랙잭
  • Part 89 - 백준(2904번): 수학은 너무 쉬워
  • Part 90 - 백준(2986번): 파스칼
  • Part 91 - 백준(3015번): 오아시스 재결합
  • Part 92 - 백준(3190번): 뱀
  • Part 93 - 백준(3190번): 벽 부수고 이동하기
  • Part 94 - 백준(4195번): 친구 네트워크
  • Part 95 - 백준(4948번): 베르트랑 공준
  • Part 96 - 백준(4963번): 섬의 개수
  • Part 97 - 백준(4991번): 로봇 청소기
  • Part 98 - 백준(5373번): 큐빙
  • Part 99 - 백준(5437번): 불
  • Part 100 - 백준(6171번): 땅따먹기
  • Part 101 - 백준(6603번): 로또
  • Part 102 - 백준(6603번): 로또
  • Part 103 - 백준(7562번): 나이트의 이동
  • Part 104 - 백준(7568번): 덩치
  • Part 105 - 백준(7576번): 토마토
  • Part 106 - 백준(7579번): 앱
  • Part 107 - 백준(7785번): 회사에 있는 사람
  • Part 108 - 백준(9012번): 괄호
  • Part 109 - 백준(9020번): 골드바흐의 추측
  • Part 110 - 백준(9184번): 신나는 함수 실행
  • Part 111 - 백준(9251번): LCS
  • Part 112 - 백준(9421번): 소수상근수
  • Part 113 - 프로그래머스: 가장 큰 정사각형 찾기
▼ 목록 보기

골드2 : 유니언 파인드 문제이다.

생각

이런 문제는 정말 비행기가 온다고 생각하고 푸는 것이 가장 좋은 것 같다. 그러니까 모든 문제는 시뮬레이션을 제대로 하는 것이 중요.. 총 5개의 게이트가 있다고 하자. 그 때, 4번으로 비행기가 들어온다. 그럼 4번에 배정하는 것이 맞다. 그런데 또 4번으로 들어온다. 이 문제는 4번 보다 작은 게이트에만 비행기가 들어올 수 있으므로 들어갈 수 있는 게이트는 1, 2, 3이다. 그런데, 문제 특징 상 원하는 게이트보다 작은 부분에만 들어갈 수 있으므로 가장 많은 비행기를 넣기 위해서는 원하는 게이트에서 작은 숫자를 가진 게이트 중, 숫자가 가장 큰 게이트에 들어가는 것이 최선이다.(응?) 이해를 위해 표를 그려보자.

gate 1 2 3 4 5
  0 0 0 1 0

이 상황에서 3번 게이트로 들어올 경우 두가지 그림이 가능하다.

gate 1 2 3 4 5
  1 0 0 1 0
gate 1 2 3 4 5
  0 0 1 1 0

1번 표의 경우, 만약 1번으로 들어오는 비행기가 있다고 하면, 이 비행기는 이미 게이트가 차 있으므로 들어가지 못한다. 그래서 총 숫자는 2이다. 2번 표에서는 1번 게이트가 비어있으므로 비행기를 댈 수 있다. 그 결과 정답은 3이다. 이렇게, 비행기가 들어오는 방법은 원하는 게이트보다 작거나 같은 게이트 중 비어있는 게이트에서 가장 큰 숫자를 가진 게이트 순서로 도킹해야한다.

유니온 파인드

위에서 생각한 것은, 대안 게이트를 찾는 방법이다. 그런데 생각해보면, 이 대안 게이트는 그래프로 볼 수 있다.

gate     : 1, 2, 3, 4, 5
airplain : 4, 4, 3, 1

위와 같은 순서로 비행기가 들어온다고 했을 때의 그림을 보자.

airplain 4

image

airplain 4

image

airplain 3

image

airplain 1

image

이 과정에서, 신경써주어야 하는 것은, 해당 게이트의 대안 게이트의 번호가 무엇인지이다! 무조건 처음에는 비행기가 왔을 때, 자신의 게이트에 넣는 것은 확정이므로, 처음 도킹 위치의 초기값은 자기 자신의 게이트 이다. 이제 비행기가 들어오면서, 어떠한 방법으로 이것을 업데이트 해줄지 고민하면 된다.

대안 게이트 합치기

기본적으로 대안 게이트의 위치는 나보다 하나 작은 녀석의 게이트이다. 하지만 그 위치에도 이미 도킹이 되어 있다면, 그 녀석의 대안 게이트를 또 찾아야 한다. 그러므로 대안 게이트를 찾아서 업데이트 할 때는 나보다 작은 녀석을 기반으로 하되, 그녀석의 최종적인 대안 게이트를 찾아야 한다. 유니온 파인드 알고리즘에서 이 대안 게이트는 조상에 치환되는 개념으로 볼 수 있다. 결국은 조상을 찾아서, 그 조상으로 업데이트를 진행해야 한다.

종료조건은 간단하다. 문제에서 보여주듯 원하는 게이트보다 작은 게이트들이 다 도킹이 된 상태면 종료한다. 이 조건은, 알고리즘 상에서 대안 게이트의 번호가 0이 되는 것과 동치이다. 0번 게이트는 없으므로 도킹할 수 없다.

알고리즘

  1. 초기 게이트의 위치를 대안 게이트의 번호를 자기자신의 게이트를 가리키도록 한다.
  2. 비행기가 들어올 때, 대안 게이트를 찾는다.
  3. 대안 게이트를 찾으면, 대안 게이트에 비행기가 도킹했다고 생각하고 도킹한 비행기의 숫자를 늘린다.
  4. 대안 게이트에 비행기가 들어왔으므로, 방금 도킹한 비행기의 대안 게이트를 찾는다.
  5. 만약, 대안 게이트의 번호가 0인 경우 현재까지 도킹한 비행기를 출력하고 종료한다.

Code

#include <iostream>
using namespace std;
int n, m;
int parent[100001];
int ans = 0;

int find(int gate){
    if (gate == parent[gate]){
        return gate;
    }
    return parent[gate] = find(parent[gate]);
     // 탐색하는 과정에서 있었던 대안 게이트를 최종 값으로 업데이트한다.
}

// 게이트의 최종 대안 게이트로 업데이트 한다.
void unite(int x, int y) {
    x = find(x);
    y = find(y);
    parent[x] = y;
}

int main(){
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        parent[i] = i;
    }
    for (int i = 0; i < m; i++) {
        int command;
        cin >> command;
        int empty_gate = find(command);
        if (empty_gate == 0) {
            break;
        }
        ans++;
        unite(empty_gate, empty_gate-1); // 기본적으로 나보다 하나 작은 게이트와 합친다.
    }
    cout << ans << '\n';
    return 0;
}

Reference

백준(10775번) - 공항