[BOJ] 17136 색종이 붙이기
백트래킹
이 문제는 10x10 보드에 색종이를 붙여보는 문제이다.
색종이는 각각 5개씩 있고 크기는 1x1 ~ 5x5 이다.
1이 써져있는 곳에만 색종이를 붙여야 한다.
이때 붙일 수 있는 최소 색종이의 개수를 구해야한다.
그래서 나는, 우선 메모리에 무엇을 저장해야 하는가부터 생각했다.
- 10x10 보드
- 색종이 담은 배열
그리고 어떻게 풀어나가야 할지 고민했다. 재귀를 이용하여 구현했다.
- 백트래킹 시작
- 만약 모든 공간이 0이면 최소값을 업데이트 하고 종료
- 만약 이전 최소값 보다 현재 값이 더 크다면 종료
- 1이 있는 위치를 찾아서 5부터 1까지 붙여본다. (순서는 큰 상관이 없는 것 같지만, 큰게 안 될 확률이 높으므로 큰 것 부터 제거해나가는 방식으로 생각했다)
- 색종이를 붙인 부분을 0으로 변경하고 카운트도 -1 한다음에 1로 돌아간다.
- 모든 색종이를 소모했을 때 1이 남아있다면 -1을 출력하도록 한다.
구현
나는 처음에 시간초과가 떳다. 위 알고리즘에서 1-2에 해당하는 “이전 최소값보다 현재 값이 더 크다면 종료”라는 가지치기 조건을 빼먹었기 때문이다.
백트래킹에서 이와 같이 최소값을 구할 때는 좋은 테크닉인듯 싶다.
import sys
def check(x, y, size, papers):
# 색종이가 없는 경우
if papers[size] == 0:
return False
# 보드 범위를 넘어가는 경우
if x + size > 10 or y + size > 10:
return False
for i in range(x, x+size):
for j in range(y, y+size):
# 색종이로 0인 공간은 채울 수 없음
if board[i][j] == 0:
return False
return True
def all_zero():
for i in range(10):
for j in range(10):
if board[i][j] == 1:
return False
return True
def set_number(x, y, size, num):
for i in range(x, x+size):
for j in range(y, y+size):
board[i][j] = num
def backtrack(papers):
global answer
# 판이 모두 0이면 answer
if all_zero():
answer = min(answer, 25-sum(papers))
return
if 25-sum(papers) > answer:
return
# 1의 위치를 찾음
x, y = -1, -1
for i in range(10):
if x > -1:
break
for j in range(10):
if board[i][j] == 1: # 앞에서 all zero 를 검사했기에 무조건 나옴
x, y = i, j
break
for size in range(len(papers)-1, 0, -1):
if check(x, y, size, papers):
set_number(x, y, size, 0)
papers[size] -= 1
backtrack(papers)
papers[size] += 1
set_number(x, y, size, 1)
return
def solution():
papers = [0, 5, 5, 5, 5, 5]
backtrack(papers)
if __name__ == "__main__":
board = []
answer = 26
for _ in range(10):
board.append(list(map(int, sys.stdin.readline().rstrip().split())))
solution()
print(answer if answer < 26 else -1)