문제 설명
https://www.acmicpc.net/problem/6549
히스토그램은 직사각형 여러 개가 아래쪽으로 정렬되어 있는 도형이다. 각 직사각형은 같은 너비를 가지고 있지만, 높이는 서로 다를 수도 있다. 예를 들어, 왼쪽 그림은 높이가 2, 1, 4, 5, 1, 3, 3이고 너비가 1인 직사각형으로 이루어진 히스토그램이다.

히스토그램에서 가장 넓이가 큰 직사각형을 구하는 프로그램을 작성하시오.
- 그냥 단순히 히스토그램에서 가장 넓이가 큰 직사각형을 구하면 된다.
문제 풀이
처음에는 3가지 case를 분류했다.
당장 다음 높이가 stack의 top보다 큰경우, 같은경우, 작은경우로 나누어서 처리했었다.
1. 큰 경우?
- 스택에 있는 모든 사각형의 넓이를 + 1해준다.
- 새로운 높이가 추가된다. push({height,1})
2. 같은 경우
- 모두 길이 + 1
3. 작은 경우
- 스택에 있는 큰애들은 다 지워버린다.
- 여태 가로길이만큼 새로 push.
그래서 아래 코드가 나왔다. 논리적으로도 약간 모순이 있을거같은데 일단 시간초과로 실패했다.
#include <bits/stdc++.h> using namespace std; int main(void) { ios::sync_with_stdio(0); cin.tie(0); while (true) { int n; long long ans = 0; cin >> n; if (n == 0) break; stack<pair<long long, long long>> s; //height, 가로 넓이; for (int i = 0; i < n; i++) { long long height; cin >> height; if (s.empty()) { // 비었을 때 s.push({height, 1}); ans = max(ans, 1 * height); continue; } if (height < s.top().first) { int cur = s.top().second; ans = max(ans, s.top().first * s.top().second); s.pop(); s.push({height, cur + 1}); // 가로의 넓이의 + 1와 좀전에 받은 높이. ans = max(ans, s.top().first * s.top().second); } // 같은 높이 else if (s.top().first == height) { int cur = s.top().second; s.pop(); s.push({height, cur + 1}); // 가로 +1 해주기 ans = max(s.top().first * s.top().second, ans); } // 더 높은게 들어온 경우... else { stack<pair<long long, long long>> t; while (!s.empty()) { if(s.top().first > height){ s.pop(); continue; } t.push({s.top().first, s.top().second + 1}); // 원래 있던거 +1 해서 넣어주기. s.pop(); } while (!t.empty()) { s.push(t.top()); ans = max(s.top().first * s.top().second, ans); t.pop(); } // 새로운거 추가. s.push({height, 1}); } } cout << ans << '\n'; } }
코드도 너무 길고 답도 없다.
조금 관점을 바꿔야했다.
- 모든 높이를 순회하며 스택에 인덱스를 저장하자. (인덱스를 알면 높이도 자연스레 알 수 있다.)
- 현재 높이가 스택의 top에 저장된 것보다 작을 경우에는 stack에서 pop하여 해당 높이의 최대 넓이를 계산하자.
- 이때 스택의 top 다음 인덱스부터 현재 인덱스까지의 거리가 넓이가 된다.
이렇게 되면 시간 복잡도도 O(n)으로 확실히 줄어듦을 알 수 있다.
애초에 스택에 든 것의 전제는 아직 넓이가 끊기지 않은 놈들이다. 즉 for문에서 i가 상태라고 할 때, i상태에서 아직 끊기지 않은 사각형들이기에 굳이 width를 둘 필요도 없고, 넓이 계산에 있어서 width의 시작점을 고민하는 낭비를 제거할 수 있었다.
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); while (true) { int n; cin >> n; if (n == 0) break; vector<int> heights(n); for (int i = 0; i < n; i++) cin >> heights[i]; stack<int> s; long long max_area = 0; for (int i = 0; i <= n; i++) { // 현재 높이 처리 int h = (i == n) ? 0 : heights[i]; while (!s.empty() && heights[s.top()] > h) { int height = heights[s.top()]; s.pop(); int width; if (s.empty()) { width = i; } else { width = i - s.top() - 1; } max_area = max(max_area, (long long)height * width); } s.push(i); } cout << max_area << '\n'; } return 0; }
확연히 반복되는 스택 연산이 줄어들어 나름 괜찮아졌다.
어렵다...
'Algorithm > 문제풀이' 카테고리의 다른 글
15650번: N과 M(2) (0) | 2025.01.15 |
---|---|
[백준] 3015번: 오아시스 재결합 (0) | 2024.08.27 |
[프로그래머스] Lv1. 가장 많이 받은 선물 (1) | 2024.07.26 |
[프로그래머스] Lv2. 요격 시스템 (0) | 2024.07.26 |
[백준] 12851 숨바꼭질 2(C++) (0) | 2024.04.30 |