본문 바로가기
Baekjoon

1725번 히스토그램(Python,스택)

by juhongyee 2023. 6. 5.
반응형

 

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

 

1725번: 히스토그램

첫 행에는 N (1 ≤ N ≤ 100,000) 이 주어진다. N은 히스토그램의 가로 칸의 수이다. 다음 N 행에 걸쳐 각 칸의 높이가 왼쪽에서부터 차례대로 주어진다. 각 칸의 높이는 1,000,000,000보다 작거나 같은

www.acmicpc.net

1. 문제 및 입출력

 

2. 풀이

Well-Known 문제 답게 풀이가 굉장히 다양합니다. 저는 그중에서도 스택을 이용한 풀이를 소개하려고 해요!

문제의 이해는 쉽습니다.
간단히 가장 큰 넓이의 직사각형을 히스토그램 내에서 찾으면 됩니다.

되게 Naive하게 접근해서 $O(n^2)$으로 알고리즘을 만들 수 있지만 당연히 N이 100000까지이니 불가능하겠죠.

그러니 새로운 풀이를 생각해봅시다.
먼저는 한가지 Lemma를 알아봅시다.

Lemma1

최대 넓이의 직사각형의 높이는 주어진 모든 직사각형의 높이 중 하나에 속한다.

이 문장의 의미는 우리가 최적의 정답을 구했다면 그 높이가 주어진 직사각형들의 높이 중 하나와 같을 것이라는 것입니다.
직관적으로 당연해보이는데요.

증명은 귀류법으로 해봅시다.

증명

아니라고 해봅시다.
우리에게 높이 $h_1 h_2 h_3...h_N$이 주어졌는데 여기서 최대를 구했을 때 그 직사각형의 높이가 $h_1 h_2 h_3...h_N$ 중 어느 것과도 같지 않은 상황입니다.

어떤 K와 W에 대해 K번째부터 W번째까지 직사각형을 택했을 때를 고민해봅시다.
이 때 당연히 이 최대크기의 직사각형의 높이 $h$는 $h_k,h_{k+1}...,h_{W}$들의 최소보다는 작아야합니다. 만약 크면 말이 안되겠죠?
그렇기 때문에 $h \le\min_{k\le i \le w}h_i$입니다. 그런데 우리는 그 중 높이 중 어떤 것과도 같지 않다고 했으므로 $h <min_{k\le i \le w}h_i$입니다.

이는


과 같은 상황입니다.

즉, 이 때 넓이는 $(W-K+1)h$인데
이는 $(W-K+1)h<(W-K+1)min_{k\le i \le w}h_i$ 이고

$(W-K+1)min_{k\le i \le w}h_i$ 도 조건을 만족하므로 모순입니다.

즉,

최대 넓이의 직사각형의 높이는 주어진 모든 직사각형의 높이 중 하나에 속한다.

입니다.

의미

그러므로 이제 우리는 input의 각 높이를 높이로 갖는 직사각형 중 최댓값을 갖는 직사각형들을 비교하면 됩니다!

그런데 문제는 어떻게 비교할 것인가겠죠?

스택을 활용하자.

우리는 스택에 다음과 같은 값들을 저장할 겁니다.

현재 높이보다 높이가 작거나 같은 직사각형의 index 중 최댓값

??? 뭔소린가 싶죠?
아래 그림과 같은 상황을 생각해봅시다.

처음부터 0 1 2 3 4 5 6번 index를 각 직사각형에 붙여주겠습니다.

예를 들어 4번 index의 직사각형 관점에서
현재 높이보다 높이가 작거나 같은 직사각형의 index 중 최댓값은 2입니다.

예를 들어 3번 index 직사각형 관점에서
현재 높이보다 높이가 작거나 같은 직사각형의 index 중 최댓값도 2입니다.

각 직사각형을 index 1번부터 차례대로 본다고 했을 때 스택의 최댓값에는 위의 값들이 저장되게 해줍시다.

만약 잘 성립되게 스택에 넣어준다면
높이를 유지하면서 최대의 넓이를 구할 수 있게 됩니다.

즉 현재 높이를 유지하면서 만들 수 있는 직사각형 중 최대의 넓이를 구하게 될 것입니다.
왜냐면 스택의 Top과 현재 직사각형 사이의 직사각형들의 높이는 현재 직사각형보다 항상 큼이 보장되어 있기 때문입니다.

이런 좋은 성질 때문에 우리는 한번에 가장 큰 직사각형의 넓이를 구할 수 있습니다. index를 저장해놨으니
((현재의 index)-(top의 index))*(현재 높이)로 바로 구할 수 있겠죠~

구현

여기부터는 현재의 의미가 조금 바뀝니다! 주의해서 읽어주세요.

구현 아이디어

  1. 각 index를 돌면서 높이를 받자.
  2. 만약 그 높이가 이전의 높이보다 클 경우!
  • 스택에 그냥 저장
    why? 우리가 위에서 정한 stack의 조건을 만족
    바로 직전 index에서의 높이가 작거나 같으면

현재 높이보다 높이가 작거나 같은 직사각형의 index 중 최댓값

  1. 만약 높이가 작거나 같을 경우!(핵심)
  • 스택을 pop
  • (pop한 높이) * (pop했을 때의 index)-((pop이 된 상태의 ) stack의 top의 index) 를 계산
  • 위를 stack top의 높이가 현재보다 높이나 작거나 같을 때까지 반복
  • 현재 높이를 스택에 저장

위와 같은 연산을 진행해주어도 스택의 조건이 유지됩니다!
작거나 같은게 나올 때까지 pop해주므로 강제로 조건을 만족시키고

pop할 때 나오는 높이는 우리가 정한 스택의 조건을 잘 만족하고 있으므로 풀이에 적은 방식으로 넓이를 계산할 수 있습니다.

만약 스택이 비었으면 처음부터 (pop했을 때의 index)까지가 모두 밑변이 됩니다.

  1. 만약 stack을 다 돌았을 경우?
  • 스택 pop
  • (pop한 높이) * (맨 끝의 index)-(pop한 index의 차이)

만약 stack을 다 돌았는데 stack에 빈 원소가 남았다는건 각 stack의 원소들 앞의 직사각형들은 모두 자신보다 높이가 높았음을 의미합니다.

그러므로 자신을 높이로 하는 가장 큰 직사각형의 넓이를 구해줍니다.

코드

import sys
from collections import deque

N = int(sys.stdin.readline())
stack = deque()
height = []
ans = -1
for i in range(N):
    cur = int(sys.stdin.readline())
    height.append(cur)

    while stack and height[stack[-1]]>cur:
        h = height[stack.pop()]
        w = i if not stack else i-stack[-1]-1
        ans = max(ans,h*w)
    stack.append(i)

while(stack):
    idx = stack.pop()
    h = height[idx]
    w = len(height) if not stack else len(height)-idx
    ans = max(ans,h*w)

print(ans)

느낀 점

플레 5 쉽지 않네요

~


이 문제가 제게 하나 Breakthrough가 되어 스택 문제들을 잘 풀게 만들어줬던 것 같습니다.

정말 좋은 문제가 많이 생각해볼 법하니 꼭 찬찬히 생각해보시면서 풀길 바랍니다 :)

 

'Baekjoon' 카테고리의 다른 글

백준 21909 Divisible by 3(Python)  (2) 2023.10.02
7578번 공장(Fenwick tree, python)  (0) 2023.07.08
1520번 내리막길(dps+dp)  (0) 2023.06.04
25823번 조합의 합의 합  (0) 2023.06.01
17298번 오큰수(DP,Backtracking) Python  (1) 2023.05.31