백준의 가운데를 말해요(1655) 문제이다.
[ 문제 바로가기 ]
[ 문제풀이 ]
접근 방법을 잘 모르겠어서 다른 분들의 생각을 조금 참고해서 해결한 문제이다.
먼저 2개의 힙을 사용해서 문제를 해결할 수 있다. 하나는 '최댓값이 가장 top에 위치하게 되는 최대힙', 또 하나는 '최소값이 가장 top에 위치하게 되는 최소힙'. 이 2개의 힙으로 값을 삽입하는 그 순간 순간의 중간값을 찾을 수가 있다.
2개의 힙을 이용해서 구현할 것인데, 2개의 규칙에 맞게 삽입을 해 주어야 한다.
1. 최대힙의 크기는 항상 최소힙의 크기보다 같거나 '1'만큼 더 크다.
2. 최소힙의 모든 원소는 최대힙의 모든 원소보다 항상 크거나 같아야 한다.
즉, 최소힙의 가장 top() 값은 항상 최대힙의 가장 top() 값 보다 크거나 같아야 한다.
바로 이 2가지 규칙이다. 위의 규칙에 맞게 힙에 데이터를 삽입하는 과정을 보기 전에, 아직 힙에 대해서 잘 모른다면 아래의 글을 읽고 오도록 하자.
가장 초기의 최대힙과 최소힙의 상태는 { } 일 것이다. 아무런 데이터가 존재하지 않는다.
여기에다가 문제의 예시로 나온 { 1, 5, 2, 10, -99, 7, 5 } 를 삽입하는 과정을 통해 그 과정을 알아보자. 위의 규칙을 반드시 지키면서 진행해야 한다.
첫 번째 데이터 = 1.
현재, 최대힙의 크기(0) = 최소힙의 크기(0) 이기 때문에, 이 상태에서 최소힙에 '1'을 삽입해준다면, 최소힙의 크기가 최대힙의 크기가 더 커져버리게 되므로 규칙1에 어긋나게 된다. 따라서, 이 경우에는 '최대힙'에 '1'을 삽입해주자.
★ 최대힙 = { 1 } , 최소힙 = { }
(찐하게 표시된 글씨는 해당 힙에서 가장 top()에 있는 값을 의미합니다.)
두 번째 데이터 = 5
현재, 최대힙의 크기는 1이고, 최소힙의 크기는 0이기 때문에, 이 경우에 최대힙에 '5'를 삽입해준다면, 최대힙의 크기가 최소힙의 크기보다 '2'만큼 더 커져버리기 때문에, 규칙1에 어긋나게 된다. 따라서, 이 경우에는 '최소힙'에 '5'를 삽입해주자.
최소힙에 '5'를 삽입하더라도, 2번규칙 또한 어긋나지 않기 때문에 그대로 삽입해주자.
★ 최대힙 = { 1 } , 최소힙 = { 5 }
세 번째 데이터 = 2
현재, 최대힙의 크기(1) = 최소힙의 크기(1) 이므로, 규칙1을 위반하지 않기 위해서, 최대힙에 '2'를 삽입해주자.
★ 최대힙 = { 2 , 1 } , 최소힙 = { 5 }
네 번째 데이터 = 10
규칙1을 위반하지 않기 위해서, 최소힙에 삽입을 해주자. 최소힙에 '10'을 삽입하더라도, 규칙2 또한 어긋나지 않게 된다.
★ 최대힙 = { 2 , 1 } , 최소힙 = { 5 , 10 }
다섯번째 데이터 = -99
규칙1을 위반하지 않기 위해서, 최대힙에 삽입을 해주자.
★ 최대힙 = { 2 , 1 , -99 } , 최소힙 = { 5 , 10 }
여섯번째 데이터 = 7
규칙1을 위반하지 않기 위해서, 최소힙에 삽입을 해주자. 삽입하더라도 규칙2 또한 어긋나지지 않는다.
★ 최대힙 = { 2 , 1 , -99 } , 최소힙 = { 5 , 7 , 10 }
마지막 데이터 = 5
규칙1을 위반하지 않기 위해서, 최대힙에 삽입을 해주자.
★ 최대힙 = { 5 , 2 , 1 , -99 } , 최소힙 = { 5 , 7 , 10 }
삽입하더라도, 최대힙의 top()값인 '5'는 최소힙의 top()값인 '5'보다 작거나 같기 때문에 규칙2에도 어긋나지 않는다.
이렇게 모든 데이터를 삽입해보았다.
그런데 중간값은 어떻게 찾을까 ???
본인이 위에 과정을 진행하면서, 데이터를 규칙에 맞게 하나씩 넣을 때 마다, 최대힙과 최소힙의 상태를 표현했었다.
눈에 띄게 표현하기 위해서 앞에 '★' 를 달아놓았다. 이 때, 최대힙의 top() 값들을 한번 보자.
최대힙의 top()값들을 순서대로 출력하면 그게 중간값이다.
위의 예시에는 규칙2를 위반하는 경우가 없는데 위반할 경우에는, 최대힙의 top()값과 최소힙의 top()값을 swap해주면 된다.
예를 들어서 { 1 , -1 } 을 삽입하는 경우를 보자.
중간값을 출력하라면 답은 1 , -1을 순차적으로 출력해야 한다.
먼저 최대힙에 '1'을 담는다. 그 후, 최소힙에 '-1'을 삽입한다. 그런데, 이는 규칙2를 위반하게 된다. 따라서 최대힙과 최소힙의 top()값들을 서로 바꿔주게 되면, 최대힙 = { -1 } 이 되고, 최소힙은 { 1 } 이 된다. 마찬가지로, 현재 상태에서의 최대힙의 top()값을 출력하게 되면 그 값이 중간값이 된다.
소스코드는 2개의 코드를 첨부하겠다. 하나는 Priority_Queue를 이용한 방법과, 또 한 가지는 Heap을 직접 구현해서 해결한 코드이다.
[ Priority_Queue Ver 소스코드 ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #include<iostream> #include<queue> #define endl "\n" #define MAX 100010 using namespace std; int N; int Arr[MAX]; void Input() { cin >> N; for (int i = 0; i < N; i++) cin >> Arr[i]; } void Solution() { priority_queue<int> Max_PQ, Min_PQ; for (int i = 0; i < N; i++) { if (Max_PQ.size() > Min_PQ.size()) Min_PQ.push(-Arr[i]); else Max_PQ.push(Arr[i]); if (Max_PQ.empty() == false && Min_PQ.empty() == false) { if (Max_PQ.top() > -Min_PQ.top()) { int Max_Value = Max_PQ.top(); int Min_Value = -Min_PQ.top(); Max_PQ.pop(); Min_PQ.pop(); Max_PQ.push(Min_Value); Min_PQ.push(-Max_Value); } } cout << Max_PQ.top() << endl; } } void Solve() { Input(); Solution(); } int main(void) { ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); //freopen("Input.txt", "r", stdin); Solve(); return 0; } | cs |
[ Heap 직접구현 Ver 소스코드 ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | #include<iostream> #include<algorithm> #define endl "\n" #define MAX 100010 using namespace std; int N, Max_Heap_Idx, Min_Heap_Idx; int Arr[MAX]; int Max_Heap[MAX]; int Min_Heap[MAX]; void Heap_push(int data, bool T) { if (T == true) { int Idx = ++Max_Heap_Idx; Max_Heap[Idx] = data; while ((Idx != 1) && (data > Max_Heap[Idx / 2])) { swap(Max_Heap[Idx], Max_Heap[Idx / 2]); Idx = Idx / 2; } } else { int Idx = ++Min_Heap_Idx; Min_Heap[Idx] = data; while ((Idx != 1) && (data < Min_Heap[Idx / 2])) { swap(Min_Heap[Idx], Min_Heap[Idx / 2]); Idx = Idx / 2; } } } int Heap_pop(bool T) { if (T == true) { int Result = Max_Heap[1]; Max_Heap[1] = Max_Heap[Max_Heap_Idx--]; int Parent = 1; int Child; while (1) { Child = Parent * 2; if (Child + 1 <= Max_Heap_Idx && Max_Heap[Child] < Max_Heap[Child + 1]) Child++; if (Child > Max_Heap_Idx || Max_Heap[Child] < Max_Heap[Parent]) break; swap(Max_Heap[Child], Max_Heap[Parent]); Parent = Child; } return Result; } int Result = Min_Heap[1]; Min_Heap[1] = Min_Heap[Min_Heap_Idx--]; int Parent = 1; int Child; while (1) { Child = Parent * 2; if (Child + 1 <= Min_Heap_Idx && Min_Heap[Child] > Min_Heap[Child + 1]) Child++; if (Child > Min_Heap_Idx || Min_Heap[Child] > Min_Heap[Parent]) break; swap(Min_Heap[Child], Min_Heap[Parent]); Parent = Child; } return Result; } int Get_Size(bool T) { if (T == true) return Max_Heap_Idx; return Min_Heap_Idx; } int Top(bool T) { if (T == true) return Max_Heap[1]; return Min_Heap[1]; } void Input() { cin >> N; for (int i = 0; i < N; i++) cin >> Arr[i]; } bool Empty(bool T) { if (T == true) { if (Max_Heap_Idx == 0) return true; return false; } if (Min_Heap_Idx == 0) return true; return false; } void Solution() { for (int i = 0; i < N; i++) { int Max_Heap_Size = Get_Size(true); int Min_Heap_Size = Get_Size(false); if (Max_Heap_Size > Min_Heap_Size) Heap_push(Arr[i], false); else Heap_push(Arr[i], true); if (Empty(true) == false && Empty(false) == false) { if (Top(true) > Top(false)) { int Max_Value = Heap_pop(true); int Min_Value = Heap_pop(false); Heap_push(Min_Value, true); Heap_push(Max_Value, false); } } cout << Top(true) << endl; } } void Solve() { Input(); Solution(); } int main(void) { ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); //freopen("Input.txt", "r", stdin); Solve(); return 0; } | cs |
'[ BOJ Code ] > # BOJ -' 카테고리의 다른 글
[ 백준 10999 ] 구간합 구하기2 (C++) (1) | 2020.05.27 |
---|---|
[ 백준 1280 ] 나무 심기 (C++) (0) | 2020.05.26 |
[ 백준 11658 ] 구간합 구하기3 (C++) (0) | 2020.05.24 |
[ 백준 2243 ] 사탕상자 (C++) (0) | 2020.05.22 |
[ 백준 5676 ] 음주코딩 (C++) (0) | 2020.05.20 |