문제 출처 및 풀이 코드
(Backjoon 12100) 2048 (Easy)
깃헙 코드
문제 요약
- 최소 1에서 최대 20 크기의 정사각형 보드에 2의 거듭제곱(최소 2, 최대 1024)의 숫자가 놓인다.
- 빈 칸은 0으로 나타낸다.
- 보드를 상하좌우로 기울일 수 있으며 기울였을 떄 같은 숫자를 만나면 두 숫자를 합쳐 하나의 숫자가 된다.
- 한 번의 이동에서 (중요) 숫자들은 각각 한번 이상 합쳐지지 않는다.
- 한 번 보드를 기울여서 이동하면 빈칸이 없도록 이동해야한다.
- 보드는 최대 5번 기울일 수 있다.
- 보드를 최대 5번 기울이는 동안 발생할 수 있는 숫자 중에 가장 높은 숫자를 구하는 문제
문제 풀이
- 움직임의 제한 상황이 존재하고 상호작용할 요소가 많이 존재하므로 모든 경우의 수를 따져보는 방법으로 BFS를 선택했다.
- 이전 문제와 마찬가지로 이동 방향 기준으로 가장 먼 쪽에 위치한 숫자부터 순서대로 이동하면 된다.
- 가장 헤맨 지점은, 한 번의 이동으로는 각각의 숫자가 한 번 이상 합쳐지지 않는 데 이 점을 확인하지 못 해서 헤맸다.
코드
from copy import deepcopy
from collections import deque
VY, VX, Y_LIST_OD, X_LIST_OD = 0, 1, 2, 3
dir = [(1, 0, -1, 1),
(-1, 0, 1, 1),
(0, -1, 1, 1),
(0, 1, 1, -1)]
def toward(_board, direction):
board = deepcopy(_board)
n = len(board)
bs = [[False for _ in range(n)] for _ in range(n)]
dy, dx = direction[VY], direction[VX]
list_order_y, list_order_x = direction[Y_LIST_OD], direction[X_LIST_OD]
order = ["Y", "X"] if dy else ["X", "Y"]
range_ = {"Y": list(range(n))[::list_order_y],
"X": list(range(n))[::list_order_x]}
changed = False
for fst_order in range_[order[0]][1:]:
for sec_order in range_[order[1]]:
y, x = (fst_order, sec_order) if dy else (sec_order, fst_order)
if board[y][x] == 0:
continue
value = board[y][x]
while True:
if (y+dy >= 0) and (y+dy < n) and (x+dx >= 0) and (x+dx < n):
next_point = board[y+dy][x+dx]
if next_point == 0:
board[y][x], board[y+dy][x +
dx] = board[y+dy][x+dx], board[y][x]
bs[y][x], bs[y+dy][x+dx] = bs[y+dy][x+dx], bs[y][x]
else:
if (next_point == value) and (bs[y+dy][x+dx] == False) and (bs[y][x]==False):
board[y][x], board[y+dy][x+dx] = 0, 2*value
bs[y][x], bs[y+dy][x+dx] = False, True
value *= 2
else:
break
y, x = y+dy, x+dx
changed = True
else:
break
return board, max(map(max, board)), changed
def bfs(board_init):
q = deque()
visited = [board_init]
q.append([board_init, 0])
max_value = -1
while q:
board, cnt = q.popleft()
for d in dir:
next_board, _max, changed = toward(board, d)
max_value = max(max_value, _max)
if cnt + 1 < 5:
if changed and (next_board not in visited):
q.append([next_board, cnt + 1])
visited.append(next_board)
return max_value
N = int(input())
board = [list(map(int, input().split())) for _ in range(N)]
print(bfs(board))
이 번에 새로 배운 것
- copy를 사용하는 경우, python의 iterable 내부에 iterable이 존재하는 경우, 그 iterable을 통째로 교체해야 변경사항이 copy한 것에만 적용됨. (copy를 사용한 경우에는 외부 iterable만 copy한 것이고 내부 요소들은 이전 것에서부터 참조한 것이기 때문)