상세 컨텐츠

본문 제목

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

자료구조와 알고리즘/백준

by Banjosh 2023. 10. 15. 22:09

본문

문제

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

예를 들어 백준이가 동생에게 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줄에 걸쳐 백준이의 동생이 말해야 하는 수를 순서대로 출력한다.

예제 입력 1 복사

7
1
5
2
10
-99
7
5

예제 출력 1 복사

1
1
2
2
2
2
5

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


 

이 문제는 가운데 값을 어떻게 빨리 찾느냐가 중요한 문제이다.

그냥 벡터를 이용해서 해야지, 혹은 정렬하면서 해야지 하면 시간 초과에 빠질 수 밖에 없다. 숫자 하나를 받을 때마다 가운데 값을 찾아야 하기 때문에 정렬에 유리한 우선순위 큐를 사용해야 된다.

 우선순위 큐를 사용하면 숫자 하나 추가할 때마다 logN이라는 짧은 시간에 정렬이 가능하니 정렬 문제는 해결이 됐다. 이제 가운데 값을 어떻게 빨리 찾느냐가 중요한데, 우선순위 큐 1개를 사용할 경우 가운데 값을 보려면 가운데 값에 도달할 때까지 원소를 하나하나 제거해야하므로 시간이 오래걸릴 수 밖에 없다. 그래서 우리는 우선순위 큐 2개를 이용해서 가운데 값을 쉽게 찾을 수 있게 할 것이다.

전체 원소의 하위 50%의 값은 max 우선순위 큐에 상위 50% 값은 min 우선순위 큐에 넣어 가운데 값을 max 우선순위 큐의 top에서 확인 할 수 있게 하면 시간복잡도가 굉장히 줄어들 수 있다.

(max 우선순위 큐는 가장 큰 수 부터 pop되는 우선순위 큐이고, min 우선순위 큐는 가장 작은 수 부터 pop되는 우선순위 큐이다.)

 

우선순위 큐 2개는 다음 규칙에서 관리된다.

 

1. 숫자 num이 입력된 경우

      a. 우선순위 큐 2개 중 1개라도 비어있으면 num을 max에 push한다.

      b. a의 경우가 아니라면 num > min.top() 인 경우 min에 push한다.

           (num > min.top()인 경우는 num이 min에서 가장 작은 값보다 크므로 min 큐에 들어가야하는 경우이다)

      c. b의 경우가 아니라면 max에 push 한다.

2. max와 min의 크기는 항상 (max == min) 또는 (max == min + 1) 인 상태를 유지한다.

       (이렇게 유지하면 중간값이 늘 max.top()이 된다.)

3. max.top()을 출력한다.

 

예제 입력 값을 통해 어떻게 문제를 풀었는지 확인해보자.

 

1 입력

규칙 1.a 에 따라 max나 min이 비어있을 경우 max에 숫자를 넣는다.

max = { 1 }

min = { }

규칙 3 에 따라 마지막엔 max.top()을 출력한다. 

결과 1 출력

 

5 입력

규칙 1.a 에 따라 max에 숫자를 넣는다.

max = { 1 , 5 }

min = { }

규칙 2 에 따라 max.top() (max에서 가장 큰 값)을 min에 옮긴다.

max = {1}

min = {5}

규칙 3 에 따라 max.top()인 1을 출력한다.

결과 1 출력

 

2 입력

규칙 1.c 에 따라 max에 숫자를 넣는다.

max = { 1 , 2 }

min = { 5 }

규칙 3 에 따라 max.top()인 2를 출력한다.

결과 2 출력

 

10 입력

규칙 1.b 에 따라 min에 숫자를 넣는다.

max = { 1 , 2 }

min = { 5, 10 }

규칙 3 에 따라 max.top()인 2를 출력한다.

결과 2 출력

 

-99 입력

규칙 1.c 에 따라 max에 숫자를 넣는다.

max = { -99, 1 , 2 }

min = { 5, 10 }

규칙 3 에 따라 max.top()인 2를 출력한다.

결과 2 출력

 

7 입력

규칙 1.b 에 따라 min에 숫자를 넣는다.

max = { -99, 1 , 2 }

min = { 5, 7, 10 }

규칙 3 에 따라 max.top()인 2를 출력한다.

결과 2 출력

 

5 입력

규칙 1.c 에 따라 max에 숫자를 넣는다.

max = { -99, 1 , 2 , 5}

min = { 5, 7, 10 }

규칙 3 에 따라 max.top()인 5를 출력한다.

결과 5 출력

 

전체적인 틀은 위 규칙에 나와있는 것과 같고 구체적인 세세한 구현은 다음 코드에서 확인하자.

 

#include <iostream>
#include <queue>

using namespace std;

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

    int n, num;
    cin >> n;
	priority_queue<int, vector<int>, greater<int>> min;
	priority_queue<int> max;
	for (int i = 0; i < n; i++)
	{
		cin >> num;
		if (max.size() > 0 && min.size() > 0)
		{
			if (min.top() < num)
				min.push(num);
			else
				max.push(num);
		}
		else
			max.push(num);
		while (max.size() < min.size())
		{
			max.push(min.top());
			min.pop();
		}
		while (max.size() > min.size() + 1)
		{
			min.push(max.top());
			max.pop();
		}
		cout << max.top() << "\n";
	}
}

관련글 더보기