Date:

1) 문제 링크

https://www.acmicpc.net/problem/1725

2) 접근

kks227님의 블로그에서 분할 정복의 예제로 배운 문제라 딱히 접근 방식이라 할 게 없었다…
이 문제를 분할 정복으로 풀 수 있겠다는 확신을 가지려면

1) 히스토그램을 쪼개도 각 구간에서 최대 넓이를 구하는 방식에 영향을 주지 않는다.
2) 최대 넓이를 구하는 구간이 정해져 있을 때, 그 구간 내의 어떤 막대에서 시작하든 최대 넓이값은 변화가 없다.

이 두 가지 생각을 증명할 수 있어야 한다고 생각한다.

그리고 histogram(s, e) 함수는 구간 $[s, e)$에 대한 것임을 항상 염두에 둘 것. 구간 $[s, e]$가 아니다!

3) 풀이

#include <iostream>
#include <algorithm>
using namespace std;

int N, H[100000];

int histogram(int s, int e){
    if(s == e) { return 0; }
    if(s+1 == e) { return H[s]; }

    int mid = (s+e)/2;
    int result = max(histogram(s, mid), histogram(mid, e));

    int w=1, h=H[mid], l=mid, r=mid;
    while(r-l+1 < e-s){
        int p = (l>s) ? min(h, H[l-1]) : -1;
        int q = (r<e-1) ? min(h, H[r+1]) : -1;

        if(p >= q){
            h = p;
            --l;
        }
        else{
            h = q;
            ++r;
        }
        result = max(result, ++w*h);
    }
    return result;
}

int main(void){
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    cin >> N;
    for(int i=0; i<N; ++i){
        cin >> H[i];
    }
    cout << histogram(0, N);
    return 0;
}

image

시간복잡도는 $O(NlogN)$.

4) 피드백

gmldnd0418님의 코드.
8ms짜리이다. 0ms 수준의 코드는 입출력에서 시간을 줄인 걸로 보였고, 8ms는 stack을 이용해서 시간복잡도 $O(N)$에 푸는 코드였다.

https://j2wooooo.tistory.com/74
https://private-space.tistory.com/12

위 블로그에 자세한 설명이 있으니 참고.
핵심은 스택에 들어있는 막대보다 다음의 막대가 크거나 같으면 그냥 스택에 넣고, 그게 아니라면 현재 들어있는 막대 중 다음에 넣을 막대보다 큰 막대들을 전부 빼내며 넓이를 계산한 후 스택에 넣는 것이라고 한다.

image

이런 식으로 쓰-윽 쓸고 지나가면서 해를 구하는 알고리즘을 스위핑 알고리즘이라고 한다. 그리디 알고리즘의 일종인 듯…?

Leave a comment