연습
두 개의 우선순위 큐를 써서 중앙값 찾기
onaeonae1
2024. 8. 23. 21:37
어떤 숫자들의 입력이 계~속 주어진다.
들어온 입력들에 대해서 중앙값을 계속 반환해야 한다.
자 그럼 이걸 어떻게 해볼까?
들어온 입력을 계속 push 하고 정렬을 한다?
-> N번 동안 N 크기의 배열을 정렬하므로 N^2logN . 따라서 시간 초과가 될 수 밖에 없다.
우리가 원하는 건 "중앙값"이다. 구체적으로 전체가 어떻게 정렬되어 있는지는 관심이 없다.
중앙값을 T 라고 했을 때, T는 정렬된 List 에서 가운데에 위치해야 한다.
하지만, 정렬을 다해주면 시간 초과. 그래서 정답 T에 대해 배열을 다음과 같이 나눠보자.
1. 무조건 작은 쪽 -> LEFT
2. 무조건 큰 쪽 -> RIGHT
LEFT에 있는 모든 애들은 RIGHT 보다 반드시 작아야 한다.
그래서 LEFT를 최대힙으로 설정. RIGHT를 최소힙으로 설정해보자
LEFT의 TOP 은 RIGHT의 TOP 보다 항상 작도록 유지 (만약 어길 시 교환)
그럼 LEFT의 TOP 이나 RIGHT의 TOP 중 하나가 답이 될 것이다 -> 세부사항은 문제 따라 다름!
이 문제를 풀어보자
https://www.acmicpc.net/problem/1655
코드는 다음과 같다.
#include<iostream>
#include<vector>
#include<queue>
#include<vector>
using namespace std;
int main() {
cin.tie(0);
cin.sync_with_stdio(false);
int N;
cin >> N;
priority_queue<int, vector<int>, less<int>> left;
priority_queue<int, vector<int>, greater<int>> right;
vector<int> answers;
for (int i = 0; i < N; i++) {
int temp;
cin >> temp;
// 일단 push. 하지만 길이는 하나만 차이나도록 해주자. (교환이 의미있도록)
if (i% 2 == 0) {
left.push(temp);
}
else {
right.push(temp);
}
// 교환
if (left.size() > 0 && right.size() > 0) {
int leftTop = left.top();
int rightTop = right.top();
if (leftTop > rightTop) {
// 야 이거 역전이다 즉시 교환
left.pop();
right.pop();
left.push(rightTop);
right.push(leftTop);
}
}
// 정답 찾기 -> 왼쪽 끝
answers.push_back(left.top());
}
for (int i : answers) {
cout << i << "\n";
}
return 0;
}