[Baekjoon] 6549. 히스토그램에서 가장 큰 직사각형

2 minute read

문제 설명

문제

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

img

히스토그램에서 가장 넓이가 큰 직사각형을 구하는 프로그램을 작성하시오.

입력

입력은 테스트 케이스 여러 개로 이루어져 있다. 각 테스트 케이스는 한 줄로 이루어져 있고, 직사각형의 수 n이 가장 처음으로 주어진다. (1 ≤ n ≤ 100,000) 그 다음 n개의 정수 h1, …, hn (0 ≤ hi ≤ 1,000,000,000)가 주어진다. 이 숫자들은 히스토그램에 있는 직사각형의 높이이며, 왼쪽부터 오른쪽까지 순서대로 주어진다. 모든 직사각형의 너비는 1이고, 입력의 마지막 줄에는 0이 하나 주어진다.

출력

각 테스트 케이스에 대해서, 히스토그램에서 가장 넓이가 큰 직사각형의 넓이를 출력한다.

예제 입력 1

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

예제 출력 1

8
4000


문제 풀이

# 분할정복


  • 분할 정복 풀이
'''
히스토그램을 중간을 경계선으로 나누면서 왼쪽, 오른쪽, 경계선 포함 직사각형 중
가장 큰 직사각형을 구한다. 
'''
import sys
input = sys.stdin.readline

def find_largest_rectangle(l,r):
    if l == r: # 너비가 1이 되었으면 높이를 반환(1*h)
        return h[l]
    
    m = (l+r) // 2
    tmp_l,tmp_r = m,m+1 # 경계선을 포함하는 가장 작은 직사각형부터 시작
    tmp_h = min(h[tmp_l],h[tmp_r])
    tmp_w = 2
    tmp_S = tmp_w*tmp_h
    # 왼쪽 또는 오른쪽으로 더 갈 수 있을 때
    while not ((h[tmp_l]==0 or tmp_l==l) and (h[tmp_r]==0 or tmp_r==r)):
        if (h[tmp_l]==0 or tmp_l==l): # 왼쪽이 막혔다면, 오른쪽으로
            tmp_h = min(tmp_h,h[tmp_r+1])
            tmp_r += 1
        elif (h[tmp_r]==0 or tmp_r==r): # 오른쪽이 막혔다면, 왼쪽으로
            tmp_h = min(tmp_h,h[tmp_l-1])
            tmp_l -= 1
        else: # 양쪽 모두 가능하다면 높이가 더 높은 쪽으로!
            if h[tmp_l-1] > h[tmp_r+1]:
                tmp_h = min(tmp_h,h[tmp_l-1])
                tmp_l -= 1
            else:
                tmp_h = min(tmp_h,h[tmp_r+1])
                tmp_r += 1

        tmp_w += 1
        tmp_S = max(tmp_S,tmp_w*tmp_h)
    # 왼쪽 영역 직사각형, 오른쪽 영역 직사각형, 경계선 포함 직사각형 중 가장 큰 면적의 직사각형을 반환
    return max(find_largest_rectangle(l,m),find_largest_rectangle(m+1,r),tmp_S)

while True:
    h = list(map(int,input().rstrip().split()))
    if h[0] == 0:  break
    print(find_largest_rectangle(1,len(h)-1))


  • 스택 풀이
import sys
input = sys.stdin.readline

while True:
    rec, n = list(map(int,input().split())), rec.pop(0)
    if n==0: break
    # 스택에는 막대들이 '높이 오름차순(같을 수 있음)'으로 쌓여 있음
    stack = []
    answer = 0

    # 왼쪽에서 오른쪽으로 탐색
    for i in range(n):
        # 1. 스택에 남아있는 블록이 있고, 
        # 2. 다음 막대의 높이가 스택의 맨 위 막대보다 작을 때
        while len(stack) != 0 and rec[i] < rec[stack[-1]]:
            temp = stack.pop()

            if len(stack) == 0: width = i # 최대 길이
            else: width = i - stack[-1] - 1 # 스택의 이전 막대 전까지의 길이
            answer = max(answer,width*rec[temp])
        stack.append(i)
    
    # 스택 안에 남아있는 블록들에 대하여, 
    while len(stack) != 0:
        temp = stack.pop()

        if len(stack) == 0: width = n
        else: width = n - stack[-1] - 1
        answer = max(answer,width*rec[temp])
    
    print(answer)

Leave a comment