[프로그래머스] 가장 큰 정사각형 찾기
문제
입력
- 2차원 배열 board
- 1 <= 행, 열 <= 1000
- board의 원소 = 0 또는 1
출력
- 1로 이루어진 가장 큰 정사각형의 넓이
규칙
- 정사각형은 축에 평행해야 함.
- 1로 이루어진 정사각형은 내부까지 1로 차있어야 함.
접근
brute-force
단순하게 모든 경우를 다 찾아보자.
주어진 board가 m행 n열이라고 하자. 문제에서 요구하는 것은 가장 큰 정사각형이므로 가능한 최대 크기 min(m, n)
부터 1
까지 한 변의 길이를 줄여나가면서 1로 이루어진 정사각형을 찾아내면 된다.
다시 말해, 한 변의 길이가 length
로 주어질 때 m행 n열의 각 원소를 좌측상단으로 하는 정사각형들 중 모든 원소가 1로 이루어져 있는 영역이 있다면 답은 length^2
이 된다.
for length in range(min(m, n), 0, -1):
for i in range(m):
for j in range(n):
if allOne(i, j, length):
return length ** 2
그런데 이 경우 for문이 5개나 중첩되어 시간복잡도가 매우 높다. cumulative sum을 사용하여 allOne
을 for문 한 개로 처리할 수도 있지만 m, n이 1000이므로 4중 for문 역시 시간초과를 피할 수 없다.
DP
이렇게 높은 시간복잡도를 가지는 문제는 수학적으로 답이 결정되거나, 기존 계산 결과를 재활용하는 문제인 경우가 많다. 이 문제의 경우 정사각형을 하나의 점과 변의 길이로 나타낼 수 있다는 점에 착안하여 결과를 재활용하는 방법을 찾을 수 있다.
board[i][j]
의 값을 “(i, j)
를 우측하단으로 하며, 1로 이루어진 정사각형 중 가장 큰 정사각형의 한 변의 길이”라고 정의하면, 다음과 같은 점화식을 얻을 수 있다.
# board[i][j]가 1인 경우
board[i][j] = min(board[i-1][j], board[i][j-1], board[i-1][j-1]) + 1
이해를 돕기 위해 예를 들어보자.
board[i-1][j] = 3, board[i][j-1] = 1, board[i-1][j-1] = 2
이 주어졌다고 가정했을 때, 각각의 의미를 시각화하면 다음과 같다.
즉, board
는 어느 영역이 1로 차있는지에 대한 정보를 담고 있는 것이다.
이걸 모두 중첩시켜서 살펴보면 다음과 같다. (당연히 기존의 board[i][j]
은 1이어야 한다.)
따라서 board[i][j]
의 새로운 값은 board[i-1][j], board[i][j-1], board[i-1][j-1]
중 가장 작은 값인 board[i][j-1]
에 1을 더한 값이라는 걸 알 수 있다.
구현
for i in range(1, len(board)):
for j in range(1, len(board[0])):
if board[i][j]:
board[i][j] = min([board[i-1][j], board[i][j-1], board[i-1][j-1]]) + 1
return max([max(li) for li in board]) ** 2
댓글남기기