문제
문제에서 상어가 물고기를 먹는 경우 중 번호의 합이 최대가 되는 경우를 구해야 하므로, 백트래킹으로 접근해야겠다는 판단이 가능하다. 또한 조건에서 물고기 번호는 유일하므로, 물고기의 정보는 리스트에 담아 인덱스로 식별할 수 있겠다.
백트래킹으로 접근하게 될 것이므로 재귀함수 형태로 문제에서 주어진 상황을 구현해야 한다. 구현하는 것은 두 가지로, 물고기를 이동시키는 것과 상어가 이동하는 것이다. 이 때 주의해야할 점은, 재귀함수 호출 시 물고기의 이동과 상어의 이동 두 행동 모두 보드를 조작한다는 것이다. 따라서 상어의 이동 뿐 아니라 물고기의 이동도 재귀함수 호출 후 원래대로 되돌려 줘야하는데, 차라리 재귀함수를 호출할 때 새로운 리스트에 복사본을 넣어 넘겨주는 편이 편하다.
어떤 부분이 풀이에 필요한 정보들을 조작할 것인지 염두하고 문제를 설계할 필요를 느낄 수 있는 문제였다. 또한 재귀함수의 각 상태에서 사용하는 정보의 불변성을 지켜줄 필요가 있으며, 전역변수를 두고 하기보다는 이쪽을 더 우선시 해야겠다는 생각이 들었다.
# N, NW, W, SW, S, SE, E, NE
dRow = [-1,-1,0,1,1,1,0,-1]
dCol = [0,-1,-1,-1,0,1,1,1]
survived = [True]*17
loc = [[0,0] for _ in range(17)]
dir = [0] * 17
board = [[0]*4 for _ in range(4)]
for row in range(4):
li = list(map(int,input().split()))
for col in range(4):
idx = col * 2
board[row][col] = li[idx]
loc[li[idx]][0],loc[li[idx]][1] = row,col
dir[li[idx]] = li[idx + 1] - 1
def update_result(result,_board,_dir,_loc,_survived):
global answer
answer = max(answer,result)
# 물고기 이동
for fish in range(1,17):
if _survived[fish]:
row,col = _loc[fish]
for j in range(8):
nDir = (_dir[fish] + j) % 8
nRow, nCol = row + dRow[nDir], col + dCol[nDir]
if 0 <= nRow < 4 and 0 <= nCol < 4 and _board[nRow][nCol] != 0:
_dir[fish] = nDir
v = _board[nRow][nCol]
if v > 0:
_loc[v][0], _loc[v][1] = row, col
_loc[fish][0], _loc[fish][1] = nRow, nCol
_board[row][col], _board[nRow][nCol] = v, fish
break
# 상어 이동
row,col = _loc[0]
d = _dir[0]
nRow,nCol = row,col
while 0 <= nRow+dRow[d] < 4 and 0 <= nCol+dCol[d] < 4:
nRow += dRow[d]
nCol += dCol[d]
v = _board[nRow][nCol]
if v > 0:
_dir[0] = _dir[v]
_loc[0][0],_loc[0][1] = nRow,nCol
_survived[v] = False
_board[row][col],_board[nRow][nCol] = -1,0
update_result(result+v,[[*elem] for elem in _board],[*_dir],[[*elem] for elem in _loc],[*_survived])
_board[row][col],_board[nRow][nCol] = 0,v
_survived[v] = True
_loc[0][0],_loc[0][1] = row,col
_dir[0] = d
answer = 0
# 상어 세팅
prey = board[0][0]
dir[0] = dir[prey]
survived[prey] = False
board[0][0] = 0
update_result(prey,board,dir,loc,survived)
print(answer)
풀이 2
문제에서 보드는 물고기가 이동하면서 한번, 상어가 이동하면서 한번 총 두번 갱신된다. 상어가 이동하는 코드를 보면 여타 백트래킹 문제와 마찬가지로 상어가 일으킨 갱신을 다시 되돌리는데, 물고기가 이동한것에 대해서는 그렇지가 않다는 사실이 중요하다. 즉 전역변수로 선언된 보드를 직접 갱신한다면, 재귀호출된 함수에서 일어난 갱신이 함수가 종료되고 다시 원래 시점으로 돌아왔을 때 되돌려질 방법이 없게될 것이다. 이때문에 물고기의 이동에서 갱신되는 locs와 board는 재귀함수 호출 시 복사해서 param으로 넣어줘야한다. 정리하자면, 반복문에서 갱신되었던것을 재귀함수가 종료된 후에 다시 되돌리는것은 상관없지만, 반복문 바깥에서 갱신된 내용에 대해서도 반드시 고려를 해줘야한다. 사실 시간제한과 메모리만 허용된다면, 잘 안된다 싶으면 일단 참조하고 있는 보드나 그외 객체를 복사해서 param으로 넣어주는것도 방법이다.
위 풀이와 다르게 물고기의 위치를 바꿀 때 값이 아닌 참조 객체를 서로 바꿔주고 있는데, 그 뒤에 locs를 따로 건드리고 있지 않기 때문에 안전한 방법이다.
물고기가 인접 칸을 탐색할 때 dir = (dir-1+j)%8 + 1 로 작성해서 엄청나게 시간을 낭비했는데, 당연하게도 이렇게 하면 방향이 45도씩 틀어지는것이 아니라 45도 -> 90도 -> 135도 이런식으로 누적된다. 원판돌리기 문제에서도 x의 배수를 구할 때 x *= 2로 해서 누적되게 해버렸는데, 이런 사소한 실수를 조심할 필요가 있겠다.
# N/A,N,NW,W,SW,S,SE,E,NE
dRow = [0,-1,-1,0,1,1,1,0,-1]
dCol = [0,0,-1,-1,-1,0,1,1,1]
# -1:상어, 0:빈칸, 1~16:물고기
board = [[[] for col in range(4)] for row in range(4)]
dirs = [0]*17
for i in range(4):
for j,v in enumerate(map(int,input().split())):
board[i][j // 2].append(v)
locs = [[0, 0] for _ in range(17)]
for row in range(4):
for col in range(4):
f = board[row][col][0]
locs[f][0], locs[f][1] = row, col
survived = [True]*17
def get_answer(_locs,_board,x,y,result):
global answer
# 물고기 이동
for i in range(1,17):
if not survived[i]:
continue
r,c = _locs[i]
dir = _board[r][c][1]
ndir = dir
for j in range(8):
ndir = (dir-1+j)%8+1
nr,nc = r+dRow[ndir],c+dCol[ndir]
if 0 <= nr < 4 and 0 <= nc < 4 and _board[nr][nc][0] >= 0:
adj = _board[nr][nc][0]
if adj > 0:
_locs[adj][0],_locs[adj][1] = r,c
_locs[i][0],_locs[i][1] = nr,nc
_board[r][c][1] = ndir
_board[r][c], _board[nr][nc] = _board[nr][nc], _board[r][c]
break
# 상어 탐색
d = _board[x][y][1]
nx,ny = x,y
while True:
# 탐색
nx += dRow[d]
ny += dCol[d]
if not (0 <= nx < 4 and 0 <= ny < 4):
answer = max(answer, result)
break
target = _board[nx][ny][0]
if target == 0:
continue
# 이동
survived[target] = False
_board[x][y][0] = 0
_board[nx][ny][0] = -1
get_answer([[*elem] for elem in _locs],[[[*elem] for elem in li] for li in _board],nx,ny,result+target)
_board[nx][ny][0] = target
_board[x][y][0] = -1
survived[target] = True
answer = 0
# 상어 출현
target = board[0][0][0]
survived[target] = False
board[0][0][0] = -1
get_answer(locs,board,0,0,target)
print(answer)
'PS > 백준' 카테고리의 다른 글
19237번 - 어른 상어 (0) | 2022.09.27 |
---|---|
19238번 - 스타트 택시 (1) | 2022.09.25 |
20061번 - 모노미노도미노 2 (0) | 2022.09.23 |
17825번 - 주사위 윷놀이 (0) | 2022.09.20 |
17837번 - 새로운 게임 2 (0) | 2022.09.17 |