[백준 6549 : JAVA] 히스토그램에서 가장 큰 직사각형 / 세그먼트 트리 +분할정복
문제
풀이
해당 문제는 두 가지 방법으로 풀 수 있다.
- 스택
- 세그먼트 트리 + 분할정복
해당 풀이는 두 번째 방법이다.
먼저, N=7의 길이를 가지는 수열에 대해 각 세그먼트 트리의 노드에 저장될 구간은 다음과 같다.
세그먼트 트리는 구간 합에 대한 문제를 통해 처음 배우기 때문에 각 노드에 어떤 값을 저장해야할지 감이 오지 않을 수 있다. (난 그랬다..)
세그먼트 트리에서의 핵심은 구간에 대한 어떤 값을 저장한다는 것이다.
이제 루트 노드에 1~7번째 구간에 대한 값을 저장해야하는데, 어떤 값을 저장해야할까?
첫 번째로 생각해볼 수 있는 값은 '넓이' 값이다.
최소 높이를 따라야하기 때문에 1~7번째 구간에서의 넓이 값은 (가로: 1 * 7) * (세로: 1) = 7 이다.
그런데, 넓이를 구하기 위해선 최소 높이를 알아야하기 때문에 문제는 다시 원점으로 돌아오게 된다.
따라서, 두 번째로 생각해볼 수 있는 값은 '해당 구간에서의 최소 높이' 값이다.
최소 높이만 안다면 넓이는 쉽게 구해지기 때문이다. 하지만, 이 값을 저장하게 되면 최대 넓이를 찾기 위한 분할 정복을 사용할 수 없다. 따라서 최소 높이를 기준으로 좌측과 우측을 분할 정복해가며 최대 넓이를 가지는 사각형을 찾아내기 위해선 해당 구간에서의 최소 높이를 가지는 값의 위치(인덱스) 값이다.
위 그림처럼 최소 높이를 가지는 위치를 기준으로 좌측과 우측에 대해 분할정복을 수행하여 최대 넓이를 찾는다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
private static final int INF = 1_000_000_000;
private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
private static int N;
private static int[] arr;
private static int[] tree;
public static void main(String[] args) throws IOException {
StringBuilder sb = new StringBuilder();
while (true) {
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
if (N == 0) break;
arr = new int[N + 1];
for (int i = 1; i <= N; i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
tree = new int[N * 4];
init(1, N, 1);
sb.append(getMaxWidth(1, N)).append("\n");
}
System.out.println(sb);
br.close();
}
// 세그먼트 트리의 각 노드엔 담당하는 구간의 최솟값을 가지는 인덱스를 저장한다.
private static int init(int start, int end, int node) {
if (start == end) return tree[node] = start;
int mid = (start + end) / 2;
int leftMinIndex = init(start, mid, node * 2);
int rightMinIndex = init(mid + 1, end, node * 2 + 1);
return tree[node] = arr[leftMinIndex] < arr[rightMinIndex] ? leftMinIndex : rightMinIndex;
}
// i~j 구간에서 최솟값을 가지는 인덱스를 반환한다.
private static int query(int start, int end, int node, int i, int j) {
if (i > end || j < start) return INF;
if (i <= start && end <= j) return tree[node];
int mid = (start + end) / 2;
int leftMinIndex = query(start, mid, node * 2, i, j);
int rightMinIndex = query(mid + 1, end, node * 2 + 1, i, j);
if (leftMinIndex == INF) return rightMinIndex;
else if (rightMinIndex == INF) return leftMinIndex;
else return arr[leftMinIndex] < arr[rightMinIndex] ? leftMinIndex : rightMinIndex;
}
// i~j 구간에서의 최대 넓이를 찾는 메서드.
private static long getMaxWidth(int i, int j) {
long maxWidth, tmpWidth;
int minIndex = query(1, N, 1, i, j);
// 최소 높이를 바탕으로 넓이 계산.
maxWidth = (long) (j - i + 1) * (long) arr[minIndex];
// 왼쪽 존재 ?
if (i < minIndex) {
tmpWidth = getMaxWidth(i, minIndex - 1);
maxWidth = Math.max(maxWidth, tmpWidth);
}
// 오른쪽 존재 ?
if (minIndex < j) {
tmpWidth = getMaxWidth(minIndex + 1, j);
maxWidth = Math.max(maxWidth, tmpWidth);
}
return maxWidth;
}
}
'Algorithm > Baekjoon' 카테고리의 다른 글
[백준 2529 : JAVA] 부등호 / 백트래킹 (0) | 2020.11.08 |
---|---|
[백준 15684 : JAVA] 사다리 조작 / 백트래킹 (0) | 2020.11.08 |
[백준 2842 : JAVA] 집배원 한상덕 / BFS + 투 포인터 (0) | 2020.10.27 |
[백준 2531 : JAVA] 회전 초밥 / 투 포인터 (0) | 2020.10.26 |
[백준 1854 : JAVA] K번째 최단경로 찾기 / 다익스트라 (1) | 2020.10.23 |
댓글
이 글 공유하기
다른 글
-
[백준 2529 : JAVA] 부등호 / 백트래킹
[백준 2529 : JAVA] 부등호 / 백트래킹
2020.11.08 -
[백준 15684 : JAVA] 사다리 조작 / 백트래킹
[백준 15684 : JAVA] 사다리 조작 / 백트래킹
2020.11.08 -
[백준 2842 : JAVA] 집배원 한상덕 / BFS + 투 포인터
[백준 2842 : JAVA] 집배원 한상덕 / BFS + 투 포인터
2020.10.27 -
[백준 2531 : JAVA] 회전 초밥 / 투 포인터
[백준 2531 : JAVA] 회전 초밥 / 투 포인터
2020.10.26