[백준 12100 : PYTHON] 2048 (Easy)
문제 풀기 : 12100번
문제
한 번의 이동은 보드 위에 있는 전체 블록을 상하좌우 네 방향 중 하나로 이동시킨다.
같은 값을 갖는 두 블록이 충돌하면 두 블록은 하나로 합쳐지게 된다.
한 번의 이동에서 이미 합쳐진 블록은 또 다른 블록과 다시 합쳐질 수 없다. (실제 게임에서는 이동을 한 번 할 때마다 블록이 추가되지만, 이 문제에서 블록이 추가되는 경우는 없다)
보드의 크기는 N×N 이다.
보드의 크기와 보드판의 블록 상태가 주어졌을 때, 최대 5번 이동해서 만들 수 있는 가장 큰 블록의 값을 구하라.
문제풀이
이 문제는 dfs, bfs 둘 다 사용해서 풀 수 있는 문제로 필자는 dfs로 접근해서 문제를 풀었다.
dfs 코드 작성은 쉽다. 상, 하, 좌, 우로 dfs를 돌리고 5번을 다 돌렸을 때 배열에서 가장 큰 값의 요소를 리턴해주면 4방향의 리턴값들 중 다시 최대값을 리턴하는 것이다.
가장 어려웠던 부분은 4방향으로 움직이는 함수들을 만드는 것이었다.
위로 움직였을 때의 상태를 예시로 들자면, 열순으로 두번째 행부터 탐색해주어야 한다. 이렇게 해주는 이유는 포인터를 사용하기 위함이다. 포인터는 가장 맨 앞쪽부터 위치해서 포인터가 가리키는 숫자들과 탐색되어지는 숫자들을 비교해 값을 변경하는 용도를 쓰여질 것이다. 이렇게 하는 이유는 경우의 수들을 보면 알 수 있다.
그럼 이제 경우의 수를 따져야 한다. 경우의 수는 3가지로 나뉠 수 있다.
- 포인터가 가리키는 수가 0일 때
- 포인터가 가리키는 수가 현재 위치의 수와 같을 때
- 포인터가 가리키는 수가 현재 위치의 수와 다를 때
첫 번째, 포인터가 가리키는 수가 0이라면 그 자리에 0이 아닌 수를 배치시켜야 하므로 현재 탐색한 수가 0이 아니면 포인터가 가리키는 곳에 탐색한 수를 넣는다. 현재 위치는 0으로 비워준다.
두 번째, 포인터가 가리키는 수가 현재 위치와 같다면 포인터의 수를 2배로 만들고 현재 위치는 0으로 만든 후에 포인터를 하나 증가시킨다. 포인터를 증가시키는 이유는 같은 수가 여러 개 연달아 있을 때 중복으로 더해질 수 있기 때문이다.
세 번째, 포인터가 가리키는 수가 현재 위치의 수와 다르면 포인터만 하나 증가시킨다.
전체코드
import sys
from copy import deepcopy
input = sys.stdin.readline
# INPUT
n = int(input())
board = [list(map(int, input().split())) for _ in range(n)]
answer = 0
# UP
def up(board):
for j in range(n):
pointer = 0
for i in range(1, n):
if board[i][j]:
tmp = board[i][j]
board[i][j] = 0
# 포인터가 가리키는 수가 0일 때
if board[pointer][j] == 0:
board[pointer][j] = tmp
# 포인터가 가리키는 수와 현재 위치의 수가 같을 때
elif board[pointer][j] == tmp:
board[pointer][j] *= 2
pointer += 1
# 포인터가 가리키는 수와 현재 위치의 수가 다를 때
else:
pointer += 1
board[pointer][j] = tmp
return board
# DOWN
def down(board):
for j in range(n):
pointer = n - 1
for i in range(n - 2, -1, -1):
if board[i][j]:
tmp = board[i][j]
board[i][j] = 0
if board[pointer][j] == 0:
board[pointer][j] = tmp
elif board[pointer][j] == tmp:
board[pointer][j] *= 2
pointer -= 1
else:
pointer -= 1
board[pointer][j] = tmp
return board
# LEFT
def left(board):
for i in range(n):
pointer = 0
for j in range(1, n):
if board[i][j]:
tmp = board[i][j]
board[i][j] = 0
if board[i][pointer] == 0:
board[i][pointer] = tmp
elif board[i][pointer] == tmp:
board[i][pointer] *= 2
pointer += 1
else:
pointer += 1
board[i][pointer]= tmp
return board
# RIGHT
def right(board):
for i in range(n):
pointer = n - 1
for j in range(n - 2, -1, -1):
if board[i][j]:
tmp = board[i][j]
board[i][j] = 0
if board[i][pointer] == 0:
board[i][pointer] = tmp
elif board[i][pointer] == tmp:
board[i][pointer] *= 2
pointer -= 1
else:
pointer -= 1
board[i][pointer] = tmp
return board
# DFS
def dfs(board, cnt):
if cnt == 5:
# 2차원 배열 요소 중 가장 큰 값 반환
return max(map(max, board))
# 상, 하, 좌, 우로 움직여 리턴한 값들 중 가장 큰 값 반환
# board를 꼭 deepcopy하여 함수를 거친 board값이 다음 함수에 들어가지 못하도록 해주어야 한다.
return max(dfs(up(deepcopy(board)), cnt + 1), dfs(down(deepcopy(board)), cnt + 1), dfs(left(deepcopy(board)), cnt + 1), dfs(right(deepcopy(board)), cnt + 1))
print(dfs(board, 0))