https://www.acmicpc.net/problem/2493
2493번: 탑
첫째 줄에 탑의 수를 나타내는 정수 N이 주어진다. N은 1 이상 500,000 이하이다. 둘째 줄에는 N개의 탑들의 높이가 직선상에 놓인 순서대로 하나의 빈칸을 사이에 두고 주어진다. 탑들의 높이는 1
www.acmicpc.net
단순히 답만 구하려면 쉬운 문제이다. 그러나 그렇게 풀면 시간복잡도가 O(N^2) 이 되어버린다.
처음에는 큐를 사용하여
6 9 5 7 4 라면 맨 뒤부터 시작해서 큐에 4를 넣고 뒤에서부터 비교하며
7을 만났을 때 => 큐의 모든 값과 비교 4 제거, 7을 큐에 삽입 현재 큐 : ( 7 )
5를 만났을 때 => 큐의 모든 값과 비교 7 을 놔두고 5를 삽입 현재 큐 : ( 7 , 5 )
9를 만났을 때 => 큐의 모든 값과 비교 5, 7 모두 제거, 9를 삽입 현재 큐 : ( 9 )
6을 만났을 때 => 큐의 모든 값과 비교 모두 불가하므로 6을 삽입 현재큐 : (9, 6)
수신이 가능하면 그 자릿수를 저장하는 식으로 구현했다. 이러면 O(N) 까지는 안되겠지만 적어도 O(N^2)는 아닐 것이라 생각하여 제출했는데 시간초과를 받았다.
그런데 잘 생각해보니 최소값만 비교하면 되었다. 다시말해 예를들어
큐에 10, 9, 8 순으로 들어있고 지금 만난 기둥의 길이가 2라고 해보자.
위의 방식으로하면 10, 9, 8을 모두 비교해야한다. 하지만 최소 우선순위큐라면?
8을 먼저 비교하고 최소인 8도 수신할 수 없는데 이보다 큰 것들을 수신할 수 있을리가 없다. -> 불필요한 비교를 줄일 수 있다.
그러나 문제를 풀고보니 문제의 분류가 스택이었다. 그리고 내 코드보다 다른 사람들의 코드가 훨씬 빨랐다. 이 문제의 정 해는 스택을 활용한 풀이었다.
이를 Monotonic stack 이라한다는데 한 번 공부해봐야겠다.
스택을 이용한 풀이의 로직은 다음과 같다.
1. 스택이 비어있다면 0
2. 스택에 지금 높이 저장
3. 다음으로 넘어가서 이전의 기둥들을 모두 비교하며 pop -> 모두 팝해도 되는 왼쪽에서 오른쪽으로 가고 있기 때문이다. 지금 기둥보다 작다면 이후 오른쪽의 기둥들의 수신을 받을 수 있을 리가 없다.
4. 수신가능하면 pop하지 않고 break -> 이후 오른쪽의 기둥들의 정보를 수신할 수 있다.
문제는 풀었지만, 정해가 아니었다.
두 코드는 모두 접근 시간복잡도 O(1)을 제외하고 꼭 N번의 pop이 일어난다.
그리고 stack의 pop은 O(1)이고, 우선순위큐의 시간복잡도는 O(logN)이다.
그러므로 내 풀이는 O(NlogN), 정 해는 O(N)의 시간복잡도를 가진다.
우선순위큐 활용 코드
#include <iostream>
#include <vector>
#include <map>
#include <queue>
using namespace std;
map<int, int> answer_map;
int tower[500001];
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int N;
priority_queue<int> wait_q;
cin >> N;
for (int i = 0; i < N; i++) {
cin >> tower[i]; // 6 9 5 7 4
}
wait_q.push(-tower[N - 1]);
for (int i = N - 2; i > -1; i--) {
int now_top = tower[i];
int iter = wait_q.size();
for (int j = 0; j < iter; j++) {
int tmp = -wait_q.top();
wait_q.pop();
if (tmp <= now_top) { // 수신가능
answer_map.insert({ tmp, i + 1 });
}
else {
wait_q.push(-tmp);
break;
}
}
wait_q.push(-now_top);
}
while (!wait_q.empty()) {
answer_map.insert({ wait_q.top(), 0 });
wait_q.pop();
}
for (int i = 0; i < N; i++) {
cout << answer_map[tower[i]] << " ";
}
}
#include <iostream>
#include <stack>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
stack<pair<int, int>> stk;
int N;
cin >> N;
for (int i = 0; i < N; i++) {
int tmp;
cin >> tmp;
while (!stk.empty()) {
int now = stk.top().second;
int idx = stk.top().first;
if (now >= tmp) { // 수신 가능
cout << idx << " ";
break;
}
else {
stk.pop();
}
}
if(stk.empty()){
cout << 0 << " ";
}
stk.push({ i + 1, tmp });
}
}
'백준 문제 풀이' 카테고리의 다른 글
백준 2073번 - 수도배관공사 (C++) (1) | 2024.01.01 |
---|---|
백준 1238번 - 파티 (C++) (0) | 2023.12.29 |
백준 20006번 - 랭킹전 대기열 (C++) (1) | 2023.12.26 |
백준 10159번 - 저울 (C++) (0) | 2023.12.26 |
백준 2458번 - 키 순서 (C++) (2) | 2023.12.26 |