본문 바로가기

트리/세그먼트 트리

히스토그램에서 가장 큰 직사각형

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

 

6549번: 히스토그램에서 가장 큰 직사각형

입력은 테스트 케이스 여러 개로 이루어져 있다. 각 테스트 케이스는 한 줄로 이루어져 있고, 직사각형의 수 n이 가장 처음으로 주어진다. (1 ≤ n ≤ 100,000) 그 다음 n개의 정수 h1, ..., hn (0 ≤ hi ≤

www.acmicpc.net

브루트포스로 O(N^2)으로 하면

100000 * 100000 -> 시간 초과

 

세그먼트 트리를 사용해야겠다.

세그먼트 트리에 저장하는 값은

해당 구간에서

"가장 작은 높이를 가진 배열의 인덱스"

 

1. 왜 인덱스?

구간을 나눠가면서 가장 큰 직사각형의 크기를 구해야한다.

구간을 나누기 위해서는

현재 어디까지 탐색을 했고,

왼쪽이나 오른쪽에 또 다른 구간이 있나 확인해야하는데,

인덱스를 저장하면 가능하다!

 

2. 왜 가장 작은 높이?

높이가 [1, 2, 3, 4, 5]인 구간을 가정해보자.

즉, 길이가 5인 구간이다.

이 때의 가장 큰 직사각형의 넓이는 5이다.

 

[2, 3]인 구간일 때는

이 때의 가장 큰 직사각형의 넓이는 4이다.

가장 작은 높이가 영향을 주는 것을 알 수 있다.

 

<소스 코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <iostream>
#include <vector>
#include <cmath>
 
typedef long long ll;
 
using namespace std;
 
vector<int> segment;
int arr[100000];
int n;
 
// 인덱스를 저장한다.
void make_segment(int node, int start, int end)
{
    if (start == end)
    {
        segment[node] = start;
        return;
    }
 
    int mid = start + (end - start) / 2;
    make_segment(node * 2, start, mid);
    make_segment(node * 2 + 1, mid + 1end);
 
    // 높이의 최소값의 인덱스를 저장
    if (arr[segment[node * 2]] <= arr[segment[node * 2 + 1]])
        segment[node] = segment[node * 2];
    else
        segment[node] = segment[node * 2 + 1];
}
 
 
// 높이 최소 인덱스 쿼리
int find_idx(int node, int start, int endint left, int right)
{
    // 탐색 범위가 쿼리 범위 밖
    if (right < start || left > end)
        return -1;
 
    if (left <= start && end <= right)
        return segment[node];
 
    int mid = start + (end - start) / 2;
    int left_idx = find_idx(node * 2, start, mid, left, right);
    int right_idx = find_idx(node * 2 + 1, mid + 1end, left, right);
 
    if (left_idx == -1)
        return right_idx;
    if (right_idx == -1)
        return left_idx;
    if (arr[left_idx] <= arr[right_idx])
        return left_idx;
    return right_idx;
}
 
ll find_area(int start, int end)
{
    int min_idx = find_idx(10, n - 1, start, end);
    ll result = (ll)(end - start + 1* (ll)arr[min_idx];
 
    // 높이가 최소인 인덱스를 기준으로 왼쪽, 오른쪽 나눈다.
    if (start <= min_idx - 1)
    {
        ll left_result = find_area(start, min_idx - 1);
        result = max(result, left_result);
    }
 
    if (min_idx + 1 <= end)
    {
        ll right_result = find_area(min_idx + 1end);
        result = max(result, right_result);
    }
 
    return result;
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);
 
    while (1)
    {
        segment.clear();
        cin >> n;
        if (n == 0)
            break;
 
        for (int i = 0; i < n; i++)
            cin >> arr[i];
 
        int h = (int)ceil(log2(n));
        int tree_size = (1 << (h + 1));
        segment.resize(tree_size);
        make_segment(10, n - 1);
        ll ans = find_area(0, n - 1);
        cout << ans << '\n';
    }
    return 0;
}
cs

'트리 > 세그먼트 트리' 카테고리의 다른 글

스위치  (0) 2024.03.14
물품보관  (0) 2024.02.20
블록쌓기게임  (0) 2024.02.19
Lazy propagation  (0) 2024.02.19
출근길 라디오  (0) 2024.02.13