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

JeongYong·2023년 4월 3일
0

Algorithm

목록 보기
130/263

문제 링크

https://www.acmicpc.net/problem/1655

문제

백준이는 동생에게 "가운데를 말해요" 게임을 가르쳐주고 있다. 백준이가 정수를 하나씩 외칠때마다 동생은 지금까지 백준이가 말한 수 중에서 중간값을 말해야 한다. 만약, 그동안 백준이가 외친 수의 개수가 짝수개라면 중간에 있는 두 수 중에서 작은 수를 말해야 한다.

예를 들어 백준이가 동생에게 1, 5, 2, 10, -99, 7, 5를 순서대로 외쳤다고 하면, 동생은 1, 1, 2, 2, 2, 2, 5를 차례대로 말해야 한다. 백준이가 외치는 수가 주어졌을 때, 동생이 말해야 하는 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 백준이가 외치는 정수의 개수 N이 주어진다. N은 1보다 크거나 같고, 100,000보다 작거나 같은 자연수이다. 그 다음 N줄에 걸쳐서 백준이가 외치는 정수가 차례대로 주어진다. 정수는 -10,000보다 크거나 같고, 10,000보다 작거나 같다.

출력

한 줄에 하나씩 N줄에 걸쳐 백준이의 동생이 말해야 하는 수를 순서대로 출력한다.

알고리즘: 자료구조, 우선순위 큐

풀이

삽입은 O(logN) 걸리고, 가운데 값을 O(1)로 찾기 위해서 최대힙과 최소힙을 사용해야 한다.
중간값 보다 크거나 같은 값은 최소힙에, 중간값보다 작은 값은 최대힙에 넣어준다면, 최소힙과 최대힙은 root에 중간값을 유지할 수 있다. 단 최소힙과 최대힙의 밸런스가 유지되어야 한다.
밸런스를 유지하기 위해서 값을 넣을 때마다 최소힙과 최대힙의 사이즈를 비교해준다.

최대힙 > 최소힙인 경우는 최대힙의 루트 값을 반환하고, 그 값을 최소힙의 넣어준다. 그리고 중간
값을 방금 넣은 값으로 갱신해준다.

최소힙 < 최대힙인 경우는 최소힙의 루트 값을 반환하고, 그 값을 최대힙의 넣어준다. 그리고 중간값을 방금 넣은 값으로 갱신해준다.

양쪽 사이즈의 밸런스가 적절히 유지되어야 최소힙과 최대힙은 중간값을 유지할 수 있다. 그렇지 않고 매우 언밸러스 하다면 각 힙의 루트 값은 더이상 중간값이 아니게 된다.

이 문제에서 짝수일 때는 중간에 있는 두 수 중에서 작은 수가 중간값이기 때문에 최소힙의 루트 값과 최대힙의 루트 값을 비교해서 더 작은 값을 출력하면 된다.
홀수일 때는 중간값 그대로 출력해주면 된다.

소스 코드

import java.io.*;
import java.util.*;

public class Main {
    static int N, mid;
    static PriorityQueue<Integer> max_heap = new PriorityQueue<>(Collections.reverseOrder());
    static PriorityQueue<Integer> min_heap = new PriorityQueue<>();
    static StringBuilder ans = new StringBuilder();
    public static void main(String args[]) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        N = Integer.parseInt(br.readLine());
        for(int i=0; i<N; i++) {
            int num = Integer.parseInt(br.readLine());
            if(i==0) max_heap.add(num);
            else {
                if(mid <= num) min_heap.add(num);
                else max_heap.add(num);
            }
            //밸런스 조정
            if(max_heap.size() > min_heap.size()) {
                int top = max_heap.poll();
                min_heap.add(top);
                mid = top;
            } else if(max_heap.size() < min_heap.size()) {
                int top = min_heap.poll();
                max_heap.add(top);
                mid = top;
            }
            if((max_heap.size() + min_heap.size()) % 2 == 0) {
                //짝수일때는 더 작은값
                int n1 = max_heap.peek();
                int n2 = min_heap.peek();
                if(n1 <= n2) {
                    ans.append(n1 + "\n");
                } else {
                    ans.append(n2 + "\n");
                }
            } else {
                ans.append(mid + "\n");
            }
        }
        System.out.println(ans.toString().trim());
    }
}

0개의 댓글