https://www.acmicpc.net/problem/8858
은행원이 현금 보유액 S를 가지고 있다.
고객이 입금을 하러 온다면, 입금 금액을 기록하고 고객이 가져온 현금을 새 봉투에 넣은 다음, 이전 입금 봉투 위에 쌓는다.
입금 봉투는 이처럼 스택 구조로 쌓이게 된다.
고객이 X원을 출금하러 온다면, 봉투 스택이 비어있는 경우 전액을 현금 보유액 S에서 지급
봉투에 든 금액들 중 가장 작은 금액보다 X가 작다면, 전액을 현금 보유액 S에서 지급
그렇지 않다면, 봉투 스택의 맨 위에서부터 하나씩 꺼낸 다음, 필요한 금액을 충당
마지막에 꺼낸 봉투에서 일부 금액만 사용하면, 남은 돈은 현금 보유액 S로 넣는다.
만약 모든 봉투를 다 사용했는데 X를 다 지급하지 못하면 남은 금액은 현금 보유액 S에서 지급
모든 입출금이 끝난 후, 현금 보유액 중 남은 금액과 봉투 스택에 남아 있는 모든 돈의 합을 구하라.
-------------------------------------------------------------------------------------------------------------------------------------------------
문제대로 구현하면 될듯
입금하러 온다면, x를 스택에 쌓아두고, 문제는 이 봉투 스택에서 최솟값을 알고 있어야,
출금하러 온 사람의 금액 x와 봉투 스택의 최솟값을 비교할 수 있음
거래 횟수가 10^6이기 때문에 O(N)으로 매번 최솟값을 찾기는 어렵다
스택의 최솟값을 알기 위해 우선순위 큐도 준비해둠
import heapq
from sys import stdin
T = int(stdin.readline())
for _ in range(T):
n,S = map(int,stdin.readline().split())
stack = []
queue = []
visited = [0]*n
for i in range(n):
x = int(stdin.readline())
if x > 0:
stack.append((x,i))
heapq.heappush(queue,(x,i))
출금하러 오는 경우는, 스택이 비어있는 경우 전액을 현금 보유액에서 지급하고
비어있지 않다면?
우선순위 큐를 이용해서 스택 내의 원소중 최솟값을 빠르게 찾아내
근데 문제는 이 값이 사용됐을 수도 있거든
그래서 visited로 표시까지 해둬
이 최솟값보다도 x가 더 작다면 현금 보유액에서 다 지급하고
x = -x
if len(stack) == 0:
S -= x
else:
while queue:
m,j = heapq.heappop(queue)
if visited[j] == 0:
break
heapq.heappush(queue,(m,j))
if m > x:
S -= x
아니라면 스택의 맨 위에서부터 금액을 하나씩 꺼내고,
x에 충당시켜
이 금액을 사용하면 우선순위 큐에서도 제거해야하는데, 이를 바로 제거하기는 어렵기 때문에 visited로 표시해둠..
스택을 다 썼는데도 x가 남아있으면? 현금 S에서 충당
이렇게 스택과 우선순위 큐, visited로 구현했음
else:
while stack:
y,j2 = stack.pop()
if y > x:
S += (y-x)
x = 0
visited[j2] = 1
break
else:
x -= y
visited[j2] = 1
if x == 0:
break
if x > 0:
S -= x
v = 0
for i in range(len(stack)):
v += stack[i][0]
print(S,v)
근데 이렇게 하면 오래걸리더라고
근데 GPT피셜 O(1)에 우선순위 큐 없이도 스택의 최솟값을 알 수 있는 방법이 있다고함

방법은 스택에 값만 넣지 말고, (값, 현재 시점의 최솟값)을 넣는다고함

혹은 스택 두개를 사용해서, 하나의 스택에는 값을 넣고, 다른 스택에서는 현재 시점의 최솟값을 넣어서 관리할 수 있다
그러면 현재 시점의 최솟값은 다른 스택의 맨 위의 값을 보면 됨
주의할 점은 스택에서 값을 빼는 경우 최솟값을 저장하는 스택에서도 값을 동시에 빼줘야함
from sys import stdin
T = int(stdin.readline())
for _ in range(T):
n,S = map(int,stdin.readline().split())
stack = []
mins = []
for _ in range(n):
x = int(stdin.readline())
if x > 0:
stack.append(x)
if len(mins) == 0:
mins.append(x)
else:
if mins[-1] > x:
mins.append(x)
else:
mins.append(mins[-1])
X를 입금할때, 스택에 X를 넣고, 현재 시점의 최솟값을 저장하는 mins 스택에는 mins가 크기가 0이라면 x가 최솟값이니 그대로 넣고
mins에 값이 존재하면, mins[-1]이 현재 시점의 최솟값이므로, mins[-1]과 x를 비교해서 x가 더 작으면 mins에 x를 넣는다.
이 x가 현재 시점의 최솟값이 되고, mins[-1]이 더 작으면 여전히 mins[-1]이 최솟값이므로 mins[-1]을 다시 append함
이러면 X를 출금할때는, 봉투 스택의 최솟값이 mins[-1]로 바로 알 수 있기 때문에 우선순위 큐를 사용한 부분이
m = mins[-1], if m > x: S -= x로 바로 구할 수 있다
그리고 봉투 스택에서 하나씩 금액을 빼서 충당할때 주의할 점은?
mins에서도 동시에 빼줘야한다는 점
else:
x = -x
if len(stack) == 0:
S -= x
else:
m = mins[-1]
if m > x:
S -= x
else:
while stack:
y = stack.pop()
mins.pop()
if y > x:
S += (y-x)
x = 0
break
else:
x -= y
if x == 0:
break
if x > 0:
S -= x
print(S,sum(stack))
'알고리즘 > 자료구조(스택,큐,해시맵)' 카테고리의 다른 글
| 스택으로 쌓아놓은 수열에서 조건을 만족하는 값들이 언제부터 다 나오는가? (0) | 2025.02.27 |
|---|---|
| O(N)으로 순서쌍 찾기1 - 기울기가 K인 직선의 개수 (0) | 2025.02.19 |
| 가장 긴 연속하는 올바른 괄호 부분 문자열의 길이 구하는 알고리즘 (0) | 2025.02.17 |
| 코딩테스트 복기 - 배열에서 특정 수보다 작은 수 중 가장 가까운 수를 찾는 방법(find nearest element than smaller one on the left side) (0) | 2023.05.22 |
| [Java]자바로 구현하는 절댓값 우선순위 큐 (0) | 2023.03.22 |