백준/풀이 회고

1655번 (힙으로 가운데값 구하기)

Lee_SH 2023. 8. 2. 16:10

1.문제

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

 

1655번: 가운데를 말해요

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

www.acmicpc.net

2.아이디어

처음 문제를 접했을 때 n개의 입력이 들어올때마다 정렬을 수행하고 정렬된 배열의 가운데값을 출력하는 코드를 작성했었다. 정렬을 하기위해 O(nlogn)의 시간복잡도가 걸리고 총 n번의 정렬을 수행해야 하므로

총 O(n * nlogn)의 시간복잡도가 걸리게되고 n = 100,000이므로 제한시간인 0.1초를 넘기게 되어 시간초과로 통과하지 못했다.

 

이후 구글링을 하여 여러 풀이를 참고한결과 힙을 통해 정렬을 수행하면 O(logn)만에 정렬을 할 수 있다는것을 알게되었고, 최대힙 + 최소힙 두가지 힙을 사용하여 가운데값을 구하는 알고리즘이 있음을 알게되어 이를 활용하여 문제를 해결하였다.

 

임의의 두 집합 A , B가 있다고 가정하자. 중간값을 찾기 위한 조건은 다음과 같다.

 

1. A의 모든 원소가 B의 모든 원소보다 작다

2. 1을 만족하고 A의 원소의 갯수가 B의 원소의 갯수와 같거나 한개 더 많을때 A의 최대값이 중간값이된다.

 

3.풀이

#include <stdio.h>
#include <iostream>
#include <queue>
#include <vector>

using namespace std;

priority_queue<int> max_heap;
priority_queue<int, vector<int>, greater<int>> min_heap;

int main() {

	ios::sync_with_stdio(false);
	cin.tie(NULL);

	int n, input;

	cin >> n;

	cin >> input;

	max_heap.push(input);

	cout << input << "\n";

	for (int i = 0; i < n-1; i++) {
		cin >> input;

		if (i % 2 == 0) { // 최소 힙에
			min_heap.push(input);
		}
		else { // 최대 힙에
			max_heap.push(input);
		}

		if (max_heap.top() > min_heap.top()) {
			int tmp = max_heap.top();
			max_heap.pop();
			max_heap.push(min_heap.top());
			min_heap.pop();
			min_heap.push(tmp);
		}

		cout << max_heap.top() << "\n";
	}

	return 0;
}

 

1번 조건을 만족하기 위해 처음에 들어오는 입력은 최대힙에 넣어주고, 이후에 들어오는 입력은 최소힙, 최대힙을 번갈아가며 넣어준다. 이를위해 i가 짝수인경우와 홀수인경우를 나눠서 번갈아 힙에 넣어주었다.

만약 최대힙의 root값보다 최소힙의 root값이 더 작은경우 1번 조건을 만족하지 못하게되므로 둘을 바꿔주는 코드를 넣어준다.