궤도

[백준] 1655번 : 가운데를 말해요 본문

💻 현생/⛓ 알고리즘

[백준] 1655번 : 가운데를 말해요

영이오 2021. 4. 4. 20:01

문제

 


풀이

 

이 문제의 시간 제한은 0.1초다.

그니까 입력이 들어오자마자 바로바로 출력을 내보내야 한다는 것이다.

입력을 받을 때를 제외하곤 단 하나의 for문도 쓸 수 없다.

 

그럼 도대체 제일 작은 값도 제일 큰 값도 아닌 중앙값을 바로바로 내보낼 것인가...

중앙값을 기준으로 그보다 작은 수는 최대 힙에 넣고 그보다 큰 수는 최소 힙에 넣는 것이다.

그렇다면 최대 힙의 top에는 중앙값보다 같거나 작은 수 중 가장 큰 수가,

최소 힙의 top에는 중앙값보다 같거나 큰 수 중 가장 작은 수가 올 것이다.

 

균형을 이루기 위해선 최대 힙과 최소 합의 원소의 갯수 차이가 0(원소의 총 갯수가 홀수)이거나 1(원소의 총 갯수가 짝수)이여야 한다.

만약 이 균형이 깨진다면 중앙값을 수정해야 한다.

 

위 상태에서 10과 11이 들어온 경우를 생각해보자

10이 들어올 때까지는 두 힙의 크기 차이가 1이기 때문에 5를 여전히 중앙값으로 쓸 수 있지만, 11이 들어오면 균형이 깨진다.

이때 최소 힙의 top에 있는 6을 새로운 중앙값으로 바꿔주고 기존의 중앙값이던 5는 최대 힙으로 넣어준다.

 

 

이번엔 0이 들어왔다고 하자.

얼핏봐선 균형이 깨지지 않는 것 같지만 그렇지 않다.

중앙값의 후보가 둘일 때는 둘 중 더 작은 값을 선택해야 한다. 이 규칙에 따르면 중앙값은 5가 된다.

그렇기 때문에 최소 힙의 크기가 최대 힙의 크기보다 큰 경우에는 그 차이가 2가 되어야 중앙값의 변화가 생기는 것과 달리,

그 반대의 경우에는 그 차이가 1만 되어도 중앙값에 변화가 생긴다.

 

따라서 최대 힙의 top에 있던 5를 새로운 중앙값으로 하고, 기존의 중앙값이던 6은 최소 힙에 넣는다.


소스코드

 

#include <iostream>
#include <queue>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    priority_queue<int> max_pq;
    priority_queue<int, vector<int>, greater<int>> min_pq; //greater면 min heap
    int N, x, mid;

    cin >> N;
    cin >> mid; //첫 입력은 중앙값
    cout << mid << '\n';
    for (int i = 1; i < N; i++) {
        cin >> x;
        if (x > mid) //중앙값보다 큰 값은 최소 힙으로
            min_pq.push(x);
        else //중앙값보다 작거나 같은 값은 최대 힙으로
            max_pq.push(x);

        int interval = max_pq.size() - min_pq.size(); //두 우선순위 큐의 크기 차이는 항상 0 또는 1이어야 함.
        if (interval > 0) { //최대 힙의 원소가 더 많은 경우
            min_pq.push(mid);
            mid = max_pq.top();
            max_pq.pop();
        } else if (interval < -1) { //최소 힙의 원소가 더 많은 경우
            max_pq.push(mid);
            mid = min_pq.top();
            min_pq.pop();
        }
        cout << mid << '\n';
    }
}

전체 소스코드다.

 

    cin >> N;
    cin >> mid; //첫 입력은 중앙값
    cout << mid << '\n';

첫번째 입력을 첫번째 중앙값으로 초기화 한다.

 

        cin >> x;
        if (x > mid) //중앙값보다 큰 값은 최소 힙으로
            min_pq.push(x);
        else //중앙값보다 작거나 같은 값은 최대 힙으로
            max_pq.push(x);

그리고 중앙값보다 큰 값은 최소 힙으로, 작거나 같은 값은 최대 힙으로 넣는다.

 

        int interval = max_pq.size() - min_pq.size(); //두 우선순위 큐의 크기 차이는 항상 0 또는 1이어야 함.
        if (interval > 0) { //최대 힙의 원소가 더 많은 경우
            min_pq.push(mid);
            mid = max_pq.top();
            max_pq.pop();
        } else if (interval < -1) { //최소 힙의 원소가 더 많은 경우
            max_pq.push(mid);
            mid = min_pq.top();
            min_pq.pop();
        }
        cout << mid << '\n';

힙에 입력을 넣은 뒤, 각 힙의 크기를 비교한다.

최대 힙의 원소가 최소 힙보다 1개라도 더 많다면 바로 중앙값을 변경하고,

최소 힙의 원소가 더 많은 경우에는 그 차이가 2가 될 때 중앙값을 변경한다.

Comments