algorithm/DFS
[백준17136/골드2] 색종이 붙이기 - Python
ayeongjin
2025. 2. 1. 17:41
https://www.acmicpc.net/problem/17136
색종이를 붙여야할 구간 (paper == 1)인 곳을 저장하고 그 부분만 순회하면서 색종이를 붙일지 말지 정하면서 백트래킹 했다.
그랬더니 가독성이 엄청 떨어지고 긴 코드가 완성됐다.
# 성공 코드 1
# python 2628 ms, 32544 KB
# 종이를 순회하면서 1이 나오면 1~5개의 색종이 하나씩 붙여보기
# 종이 붙인 곳 2로 표시
paper = [list(map(int, input().split())) for _ in range(10)]
cnt = 0 # 가려야할 1의 수
paper_1 = []
paper_cnt = [0, 5, 5, 5, 5, 5]
visited = [[False] * 10 for _ in range(10)]
for a in range(10):
for b in range(10):
if paper[a][b] == 1:
paper_1.append((a, b))
cnt += 1
result = float('inf')
def dfs(m, n):
global result
if m == cnt:
result = min(result, n)
return n
if n >= result:
return
i, j = paper_1[m]
# 이미 붙인 색종이면 다음 색종이로
if visited[i][j]:
dfs(m+1, n)
# 붙여야하면 어떤 크기의 색종이 붙일지 정하기
else:
for c in range(5, 0, -1): # 색종이 크기 5~1까지
if paper_cnt[c] <= 0:
continue
cover = []
can = True
# 색종이 붙일 수 있는지 확인
for k in range(i, i + c):
if not can: break
for l in range(j, j + c):
if not (0 <= k < 10 and 0 <= l < 10) or paper[k][l] != 1:
can = False
break
cover.append((k, l))
if can:
# 색종이 붙이기
for x, y in cover:
visited[x][y] = True
paper[x][y] = 2
paper_cnt[c] -= 1 # 색종이 차감
# 다음 색종이로
dfs(m+1, n+1)
# 색종이 띠어내기
for x, y in cover:
visited[x][y] = False
paper[x][y] = 1
paper_cnt[c] += 1 # 색종이 돌려놓기
dfs(0, 0)
if result == float('inf'):
result = -1
print(result)
호준오빠가 리스트 컴프리헨션을 잘 쓰길래 나도 따라해보려고 다시 공부했다.
https://ayeongjin.tistory.com/37
[Structures] List Comprehension과 any, all
1️⃣ 기본 문법[표현식 for 변수 in iterable]# exnumbers = [x for x in range(10)] print(numbers) # ➝ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 2️⃣ 조건 추가 (if문)[표현식 for 변수 in iterable if 조건] ✅ 예제: 짝수만 리스트에
ayeongjin.tistory.com
그리고 코드도 다시 리팩토링했다.
1. 색종이 붙였다 띠었다 하는 함수 따로 분리
2. 1~5크기의 색종이 범위 내에 붙일 수 있는지 확인하는 함수 따로 분리
3. paper를 2로 바꿔서 붙이므로 방문처리 필요 없음, visited 배열 삭제
# 성공 코드 2
# python 1016 ms, 32424 KB
paper = [list(map(int, input().split())) for _ in range(10)]
ones = [(i, j) for i in range(10) for j in range(10) if paper[i][j] == 1]
paper_cnt = [0, 5, 5, 5, 5, 5]
result = float('inf')
# size크기의 색종이 x, y 위치를 시작으로 붙일 수 있는지 확인
def can_place(x, y, size):
if x + size > 10 or y + size > 10:
return False
return all(paper[i][j] == 1 for i in range(x, x + size) for j in range(y, y + size))
# 색종이 붙이기(val == 1) 또는 떼기(val == 2)
def place_paper(x, y, size, val):
for i in range(x, x + size):
for j in range(y, y + size):
paper[i][j] = val
def dfs(idx, used):
global result
if idx == len(ones):
result = min(result, used)
return
if used >= result:
return
x, y = ones[idx]
if paper[x][y] == 2:
dfs(idx + 1, used)
return
for size in range(5, 0, -1):
if paper_cnt[size] > 0 and can_place(x, y, size):
paper_cnt[size] -= 1
place_paper(x, y, size, 2)
dfs(idx + 1, used + 1)
place_paper(x, y, size, 1)
paper_cnt[size] += 1
dfs(0, 0)
print(result if result != float('inf') else -1)