728x90
반응형
골드 Ⅳ
https://www.acmicpc.net/problem/7662
📄 문제
- 이중 우선순위 큐(dual priority queue)는 전형적인 우선순위 큐처럼 데이터를 삽입, 삭제할 수 있는 자료 구조이다.
- 전형적인 큐와의 차이점은 데이터를 삭제할 때 연산(operation) 명령에 따라 우선순위가 가장 높은 데이터 또는 가장 낮은 데이터 중 하나를 삭제하는 점이다.
- 이중 우선순위 큐를 위해선 두 가지 연산이 사용되는데, 하나는 데이터를 삽입하는 연산이고 다른 하나는 데이터를 삭제하는 연산이다.
- 데이터를 삭제하는 연산은 또 두 가지로 구분되는데
- 하나는 우선순위가 가장 높은 것을 삭제하기 위한 것이고
- 다른 하나는 우선순위가 가장 낮은 것을 삭제하기 위한 것이다.
- 정수만 저장하는 이중 우선순위 큐 Q가 있다고 가정하자.
- Q에 저장된 각 정수의 값 자체를 우선순위라고 간주하자.
Q에 적용될 일련의 연산이 주어질 때 이를 처리한 후 최종적으로 Q에 저장된 데이터 중 최댓값과 최솟값을 출력하는 프로그램을 작성하라.
💡 아이디어
- 최소힙과 최대힙을 각각 따로 구현해 준다.
- 삽입 연산이 시행될 때 최소힙, 최대힙에 각각 저장해 준다.
- 최대힙은 삽입 시 입력받은 숫자 n에 -1을 곱하고, 결과값을 출력 시에는 다시 -1을 곱한 후 출력한다.
- 삭제 연산 시 두 힙의 데이터가 동일해야 하기 때문에 삽입 시 최대힙과 최소힙을 동기화시켜야 한다.
- 다른 힙에서 이미 삭제된 숫자를 구분하여 제거하기 위해 동기화시킨다.
- 최대힙과 최소힙을 동기화시키는 방법에는 2가지가 존재한다.
- 삽입 시 숫자의 번호인 변수 i를 저장하고 삭제 시 visited에 삭제된 수의 번호를 저장한다.
- 삽입 시 숫자의 개수를 증가시키고 삭제 시 해당 숫자의 개수를 감소시킨다.
- 동기화시킨 방법에 따라 삭제 시 삭제 연산을 수행해야 하는 힙(최소 or 최대)에서 존재하지 않는 값을 정리한다.
- 최대 or 최소 값 중 존재하는 값이 등장하면 해당 힙에서 삭제한다.
- 최종적으로 남아 있는 두 힙의 값 중에 존재하지 않는 데이터를 정리한다.
- 남아 있는 데이터 중 최댓값과 최솟값을 출력한다.
"최대힙과 최소힙, 두 힙을 동기화시키는 방법"에 대해 두 가지 풀이 방법이 존재한다.
- 각 숫자의 인덱스를 저장하여 구분한다.
- 각 숫자의 개수를 저장하여 존재 여부 판단한다.
🟩 첫 번째 아이디어 (각 숫자에 번호를 부여하여 동기화)
- 삽입 시 최대힙과 최소힙에 각각 숫자에 번호를 부여하여 (숫자, 번호)의 튜플 형태로 함께 저장한다.
- 반복문이 몇 번 수행되었는지 가리키는 변수 i를 같이 저장한다.
- 즉, 인덱스로 숫자를 구분하여 저장한다.
- 삭제 된 숫자를 저장할 빈 배열을 생성한다. (삭제된 값 방문 체크)
- 삭제 시 각 숫자의 번호(i)를 visited에 저장한다.
- 다른 힙에서 삭제하기 전에 해당 숫자가 이미 삭제된 값인지 확인하기 위해 사용한다.
- 삭제 연산을 수행하기 전에 해당 숫자가 존재하는 값인지 확인한다.
- 즉, 다른 힙에서 이미 삭제된 값인지 확인한다.
- 삭제된 숫자의 번호를 저장해 놓는 visited를 활용한다.
- visited에 저장되어 있는 번호인지 확인한다.
- 저장되어 있다면 이미 삭제된 값이다.
- 저장되어있지 않다면 존재하고 있으며 삭제연산이 가능한 값이다.
- 다른 힙에서 이미 삭제된 값이라면 해당 힙에서도 제거한다.
- 4, 5번을 반복 수행하여 존재하는 값이 등장하면 삭제 연산을 수행한다.
- visited에 들어있지않은 번호가 나타나면
- 해당 값은 다른 힙에서 삭제되지 않은 존재하는 값이다.
- 삭제 연산이 수행되면 삭제된 값의 번호를 visited에 저장한다.
✅ 두 번째 아이디어 (각 숫자의 개수를 저장하여 동기화) - 최종코드
- 삽입 시 각 숫자의 개수를 visited에 {숫자: 개수}의 딕셔너리 형태로 저장한다.
- 삭제 연산을 수행하기 전에 해당 숫자의 존재 여부를 확인한다.
- visited에 저장되어 있는 숫자의 개수가
- 0 이하이면 이미 삭제되어 존재하지 않는 값이다.
- 1 이상이면 존재하며 삭제 연산이 가능한 값이다.
- 다른 힙에서 이미 삭제된 값이라면 해당 힙에서 값을 제거한다.
- 2, 3번을 반복 수행하여 존재하는 값이 등장하면 삭제 연산을 수행한다.
- 삭제 연산이 수행되면 visited에서 해당 값의 개수를 1 감소시킨다.
즉, 이 방법은 삽입 시 각 숫자의 개수를 저장하여 1개 이상 있는 지에 따라 해당 값의 존재 여부 판단한다.
- 삽입 시 해당 숫자의 개수를 증가시킨다.
- 삭제 시 해당 숫자의 개수를 감소시킨다.
📝 문제 풀이 (Code)
최종 코드
삽입 시 각 숫자의 개수를 저장하여, 두 힙에서 각 숫자의 존재 여부를 판단한다.
import sys, heapq
input = sys.stdin.readline
# 중복 제거
def de_duplication(heap):
global min_heap, max_heap
if heap == 'max_heap':
# max_heap에 값이 있고 -max_heap[0]이 삭제된 값이면
while max_heap and visited[-max_heap[0]] < 1:
heapq.heappop(max_heap)
else:
# min_heap에 값이 있고 min_heap[0]이 삭제된 값이면
while min_heap and visited[min_heap[0]] < 1:
heapq.heappop(min_heap)
t = int(input())
for _ in range(t):
k = int(input())
max_heap = []
min_heap = []
# 값의 추가, 삭제 기록
visited = {}
for i in range(k):
operation, n = input().split()
n = int(n)
if operation == 'I':
# 이미 값이 존재하면 개수만 올려줌
if visited.get(n):
visited[n] += 1
else:
heapq.heappush(min_heap, n)
heapq.heappush(max_heap, -n)
visited[n] = 1
else:
if not max_heap or not min_heap:
continue
if n == 1:
# 중복 제거
de_duplication('max_heap')
# 최댓값 삭제
if max_heap:
visited[-max_heap[0]] -= 1
else:
# 중복 제거
de_duplication('min_heap')
# 최솟값 삭제
if min_heap:
visited[min_heap[0]] -= 1
de_duplication('max_heap')
de_duplication('min_heap')
if min_heap and max_heap:
print(-max_heap[0], min_heap[0])
else:
print('EMPTY')
처음 코드
삽입 시 각 숫자의 번호를 함께 저장하여, 두 힙(최대, 최소 힙)을 동기화시킨다.
import sys, heapq
input = sys.stdin.readline
t = int(input())
for _ in range(t):
k = int(input())
max_heap = []
min_heap = []
# 해당 숫자가 존재하는지 여부 저장 (삭제시 저장)
visited = set()
for i in range(k):
operation, n = input().split()
n = int(n)
if operation == 'I':
heapq.heappush(min_heap, (n, i))
heapq.heappush(max_heap, (-n, i))
else:
if n == 1:
# max_heap 안에 숫자가 존재하고 max_heap에 존재하는 최대값이 이미 삭제된 값이면
while max_heap and max_heap[0][1] in visited:
# 이미 삭제된 값 제거
heapq.heappop(max_heap)
# max_heap에 값이 남아있으면 삭제 연산 수행
if max_heap:
# 삭제
_, idx = heapq.heappop(max_heap)
# 삭제된 숫자 번호 저장
visited.add(idx)
else:
# min_heap 안에 숫자가 존재하고 min_heap에 존재하는 최소값이 이미 삭제된 값이면
# 번호가 visited에 존재하면 이미 삭제된 값이다.
while min_heap and min_heap[0][1] in visited:
# 이미 삭제된 값 제거
heapq.heappop(min_heap)
# min_heap에 값이 남아있으면
if min_heap:
# 삭제
_, idx = heapq.heappop(min_heap)
# 삭제된 숫자 번호 저장
visited.add(idx)
# max_heap에 존재하는 이미 삭제된 최대값 삭제
while max_heap and max_heap[0][1] in visited:
heapq.heappop(max_heap)
# min_heap에 존재하는 이미 삭제된 최소 값 삭제
while min_heap and min_heap[0][1] in visited:
heapq.heappop(min_heap)
if min_heap and max_heap:
print(-max_heap[0][0], min_heap[0][0])
else:
print('EMPTY')
728x90
반응형
'Algorithm Solving > Baekjoon' 카테고리의 다른 글
[백준] 13460 구슬 탈출 2 (파이썬) (0) | 2023.09.14 |
---|---|
[백준] 17143 낚시왕 (파이썬) (0) | 2023.09.13 |
[백준] 1300 K번째 수 (파이썬) (0) | 2023.09.11 |
[백준] 15685 드래곤 커브 (파이썬) (0) | 2023.09.07 |
[백준] 12100 2048 (Easy) (파이썬) (0) | 2023.09.06 |