
백준 / 플래티넘 5 / 6549. 히스토그램에서 가장 큰 직사각형

# 인덱스 start부터 end까지 히스토그램의 최대넓이
def get_area(hist, start, end):
# 종료조건 - 밑변의 길이가 1일 때
if start == end:
return hist[start]
mid = (start + end) // 2
# 왼쪽 영역 최댓값
left = get_area(hist, start, mid)
# 오른쪽 영역 최댓값
right = get_area(hist, mid + 1, end)
# 이후 풀이에 계속
hist의 start번째부터 end번째까지 직사각형의 넓이를 구하는 함수 get_area(hist, start, end)를 만들어 봅시다.hist에는 각 직사각형의 높이가 리스트로 저장되어 있습니다.start == end (밑변의 길이가 1일 때)인 경우, 면적은 이므로, 바로 hist[start]로 높이를 반환하면 됩니다.get_area 함수를 실행합니다.left, right에 저장합시다.
left, right 중에 최댓값을 반환하면 끝...? 이라고 생각했으면 크나큰 오산입니다.
# get_area 함수 계속
# 왼쪽, 오른쪽을 가로지르는 영역 최댓값
l = mid
r = mid + 1
cross = 0 # 가로지르는 영역 중 최댓값을 저장
height = min(hist[l], hist[r]) # 현재 탐색한 막대기 중 최저높이
# 이후 풀이에 계속
탐색 준비
start ~ mid번째 막대기, mid + 1 ~ end번째 막대기로 영역을 나누었을 때l번째, 마지막 막대기를 r번째로 둘 때, 가운데부터 탐색하기 위해 l = mid, r = mid + 1로 둡니다.(밑변) * (막대기 높이 중 최솟값)이므로, 높이를 뜻하는 height 변수에는 앞으로 현재 탐색한 막대기들 중 최소 높이를 저장하겠습니다.l, r 두 막대기만 확인했으니, hist[l]와 hist[r] 중 최솟값으로 초기화합니다.cross 변수에 저장하고, 계속 갱신하겠습니다. 초깃값은 0으로 둡니다.# get_area 함수 계속
while True:
area = (r - l + 1) * height # 현재 탐색한 막대기 넓이
cross = max(area, cross)
if l <= start and end <= r: # 모든 칸을 다 탐색
break
elif l <= start: # j를 우측으로 이동
height = min(height, hist[r + 1])
r += 1
elif end <= r: # i를 좌측으로 이동
height = min(height, hist[l - 1])
l -= 1
else: # 왼쪽을 이동할지, 오른쪽을 이동할지 판단
r_height = min(height, hist[r + 1])
l_height = min(height, hist[l - 1])
if l_height > r_height:
height = l_height
l -= 1
else:
height = r_height
r += 1
return max(left, right, cross)
# get_area 함수 종료
탐색
l번째부터 r번째 막대기까지의 넓이인 area로 (r - l + 1) * height를 구하고, 현재 cross보다 크면 갱신합니다.l을 l-1로 이동해서 왼쪽 칸을 탐색하거나, r을 r+1로 이동해서 오른쪽 칸을 탐색합니다.height (막대기들 중 최소 높이)가 더 높게 유지되는 쪽으로 이동합니다.r을, 더 오른쪽으로 못 가면 무조건 l을 이동합니다.while True)left, right, cross 중 최댓값을 반환하면 됩니다.



cross의 값은 9가 됩니다.for문 2개 이용해서 넓이의 모든 경우의 수 구하면 되지 않냐고요? 시간 초과... 하하...cross)은 파란색으로 표시했으니 참고 바랍니다.
# 인덱스 start부터 end까지 히스토그램의 최대넓이
def get_area(hist, start, end):
# 종료조건 - 밑변의 길이가 1일 때
if start == end:
return hist[start]
mid = (start + end) // 2
# 왼쪽 영역 최댓값
left = get_area(hist, start, mid)
# 오른쪽 영역 최댓값
right = get_area(hist, mid + 1, end)
# 왼쪽, 오른쪽을 가로지르는 영역 최댓값
l = mid
r = mid + 1
cross = 0
height = min(hist[l], hist[r])
while True:
area = (r - l + 1) * height
cross = max(area, cross)
if l <= start and end <= r:
break
elif l <= start: # j를 우측으로 이동
height = min(height, hist[r + 1])
r += 1
elif end <= r: # i를 좌측으로 이동
height = min(height, hist[l - 1])
l -= 1
else:
r_height = min(height, hist[r + 1])
l_height = min(height, hist[l - 1])
if l_height > r_height:
height = l_height
l -= 1
else:
height = r_height
r += 1
return max(left, right, cross)
while True:
num_input = input()
if num_input == "0":
break
hist = list(map(int, num_input.split()))[1:]
print(get_area(hist, 0, len(hist) - 1))